From patchwork Sun Jun 14 02:55:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1308819 X-Patchwork-Delegate: bmeng.cn@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.a=rsa-sha256 header.s=google header.b=iUVKA3ZU; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49kzkX5WLMz9sQx for ; Sun, 14 Jun 2020 13:01:24 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 625E081DE8; Sun, 14 Jun 2020 04:57:17 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="iUVKA3ZU"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 125E681D1C; Sun, 14 Jun 2020 04:56:15 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-il1-x141.google.com (mail-il1-x141.google.com [IPv6:2607:f8b0:4864:20::141]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 0FD9481D2B for ; Sun, 14 Jun 2020 04:55:50 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sjg@chromium.org Received: by mail-il1-x141.google.com with SMTP id b5so12292169iln.5 for ; Sat, 13 Jun 2020 19:55:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=FofRJqjbXNUCcK3WeP8n2ewCHXfoFTRwLMNDRfUkXF8=; b=iUVKA3ZU8ve1e3lGAABSdTM7bhpcJnEHDhE6kdYMC/+rPMxI+U2r6PMgO2QvywAxNI KaoH2nfybJy7ZPOaKxzUOBaAmoDpCGvkWtgqC+xEZxgB6+XTy3xuGXz4NF2jpyhrdo7p fqg1WlYrXENDuHe9f1k4j7mVOY4GEhtvslie0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FofRJqjbXNUCcK3WeP8n2ewCHXfoFTRwLMNDRfUkXF8=; b=PxRPtXF/4UjrXyDA4q37pOxaPOboGxQH6NwROqc1kXEsSFNGH+F33REDC6BCxMASdQ sEY6ItUl6czlXhcgqke/aM67/Qe9B6vSsm+MIwDH/GFZc5i7jgAYWS5arTTrkvYIN4u2 imfTa/+Jtemt/7q08HZVB27+gXd7AZ3ztTv/MJN7NaqAo2RABsVhQPKxYbGElKgGl/UJ j0Sigbm60eX/nlE0qUo7ku/kPkIi4GgIRUCipipbYTZXZnO2mG+00igo81cfotaV1Ok7 1mPKweUbf3++0+ARtF+TJx8MkA0lq0XpQwIwVCcRgacxGrPl4elTR/pgCicMaYn16ZoH m8sQ== X-Gm-Message-State: AOAM532j6yTRNexC8TvTnjeFqBeeVWIHV7vueSjJMErg7+GHQ1cFY/tJ d6ejocHGMQ9uzI8opmpw1uwN+40KcobYyA== X-Google-Smtp-Source: ABdhPJxX51TTQLFAPjwU2832NgMjyPluFXOUHNWtpGT59N7Mk0FxNz42kzxZfqg9SUiQ8VvmcMJ5QQ== X-Received: by 2002:a92:b00e:: with SMTP id x14mr19429047ilh.219.1592103349441; Sat, 13 Jun 2020 19:55:49 -0700 (PDT) Received: from localhost.localdomain (c-73-14-175-90.hsd1.co.comcast.net. [73.14.175.90]) by smtp.gmail.com with ESMTPSA id 4sm5722288ilc.34.2020.06.13.19.55.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 13 Jun 2020 19:55:49 -0700 (PDT) From: Simon Glass To: U-Boot Mailing List Cc: Bin Meng , Wolfgang Wallner , Andy Shevchenko , Simon Glass Subject: [PATCH v3 25/35] acpi: Add support for a generic power sequence Date: Sat, 13 Jun 2020 20:55:13 -0600 Message-Id: <20200614025523.40183-14-sjg@chromium.org> X-Mailer: git-send-email 2.27.0.290.gba653c62da-goog In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean 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 Reviewed-by: Wolfgang Wallner --- (no changes since v1) include/acpi/acpi_device.h | 41 ++++++++++++++++ lib/acpi/acpi_device.c | 99 ++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 68 ++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index c0c96183e4..5019a79528 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -342,4 +342,45 @@ int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev); */ int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev); +/** + * acpi_device_add_power_res() - Add a basic PowerResource block for a device + * + * This includes GPIOs to control enable, reset and stop operation of the + * device. Each GPIO is optional, but at least one must be provided. + * This can be applied to any device that has power control, so is fairly + * generic. + * + * Reset - Put the device into / take the device out of reset. + * Enable - Enable / disable power to device. + * Stop - Stop / start operation of device. + * + * @ctx: ACPI context pointer + * @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g. + * PAD_CFG0_TX_STATE + * @dw0_name: Name to use for access to dw0, e.g. "\\_SB.GPC0" + * @reset_gpio: GPIO used to take device out of reset or to put it into reset + * @reset_delay_ms: Delay to be inserted after device is taken out of reset + * (_ON method delay) + * @reset_off_delay_ms: Delay to be inserted after device is put into reset + * (_OFF method delay) + * @enable_gpio: GPIO used to enable device + * @enable_delay_ms: Delay to be inserted after device is enabled + * @enable_off_delay_ms: Delay to be inserted after device is disabled + * (_OFF method delay) + * @stop_gpio: GPIO used to stop operation of device + * @stop_delay_ms: Delay to be inserted after disabling stop (_ON method delay) + * @stop_off_delay_ms: Delay to be inserted after enabling stop. + * (_OFF method delay) + * + * @return 0 if OK, -ve if at least one GPIO is not provided + */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, + 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); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 38101c8b96..912369498a 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -382,6 +382,105 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, return 0; } +/* PowerResource() with Enable and/or Reset control */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, + 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_name, + &reset, true); + if (ret) + return log_msg_ret("reset1", ret); + } + if (has_enable) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &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_name, + &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_name, + &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_name, + &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_name, + &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_name, + &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) diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index cbd0007365..3999825953 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -795,3 +795,71 @@ static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_gpio_toggle, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test writing ACPI code to output power-sequence info */ +static int dm_test_acpi_power_seq(struct unit_test_state *uts) +{ + struct gpio_desc reset, enable, stop; + const uint addr = 0xc00dc, addr_act_low = 0x80012; + const int txbit = BIT(2); + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context_size(&ctx, 400)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 0, &reset, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 1, &enable, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &stop, 0)); + ptr = acpigen_get_current(ctx); + + ut_assertok(acpi_device_add_power_res(ctx, txbit, "\\_SB.GPC0", + &reset, 2, 3, &enable, 4, 5, + &stop, 6, 7)); + ut_asserteq(0x16a, acpigen_get_current(ctx) - ptr); + ut_asserteq_strn("PRIC", (char *)ptr + 0x18); + + /* First the 'ON' sequence - spot check */ + ut_asserteq_strn("_ON_", (char *)ptr + 0x38); + + /* reset set */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x49))); + ut_asserteq(OR_OP, ptr[0x4e]); + + /* enable set */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x6e))); + ut_asserteq(OR_OP, ptr[0x73]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x97))); + ut_asserteq(NOT_OP, ptr[0x9c]); + + /* stop set (disable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xc3))); + ut_asserteq(OR_OP, ptr[0xc8]); + + /* Now the 'OFF' sequence */ + ut_asserteq_strn("_OFF", (char *)ptr + 0xe4); + + /* stop clear (enable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xf5))); + ut_asserteq(NOT_OP, ptr[0xfa]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x121))); + ut_asserteq(OR_OP, ptr[0x126]); + + /* enable clear */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x14a))); + ut_asserteq(NOT_OP, ptr[0x14f]); + + free_context(&ctx); + + return 0; +} + +DM_TEST(dm_test_acpi_power_seq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);