From patchwork Thu Mar 23 19:46:18 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Shevchenko X-Patchwork-Id: 9641855 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1CD59602D6 for ; Thu, 23 Mar 2017 19:46:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0B32A200DF for ; Thu, 23 Mar 2017 19:46:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F107B26530; Thu, 23 Mar 2017 19:46:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5AD5C200DF for ; Thu, 23 Mar 2017 19:46:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755356AbdCWTqk (ORCPT ); Thu, 23 Mar 2017 15:46:40 -0400 Received: from mga02.intel.com ([134.134.136.20]:62493 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754779AbdCWTq3 (ORCPT ); Thu, 23 Mar 2017 15:46:29 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1490298388; x=1521834388; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=vioAKYkPEbpe6I3bx6jv5Yp0uFul+dp8D6OjL0Bo3B8=; b=tq2alJi3ZunbeZyxGxE5oFdULv8mLvskkb+puil2xgjmVOTu6BW/0VxF TjbFjV2gNYoNou57z9jfD/weQ7Wqvg==; Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 23 Mar 2017 12:46:27 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,211,1486454400"; d="scan'208";a="1111551511" Received: from black.fi.intel.com ([10.237.72.28]) by orsmga001.jf.intel.com with ESMTP; 23 Mar 2017 12:46:24 -0700 Received: by black.fi.intel.com (Postfix, from userid 1003) id B8E82346; Thu, 23 Mar 2017 21:46:19 +0200 (EET) From: Andy Shevchenko To: Linus Walleij , Alexandre Courbot , linux-gpio@vger.kernel.org, Dmitry Torokhov , Hans de Goede , linux-kernel@vger.kernel.org, Mika Westerberg , Jarkko Nikula , linux-acpi@vger.kernel.org Cc: Andy Shevchenko Subject: [PATCH v1 8/8] gpio: acpi: Override GPIO initialization flags Date: Thu, 23 Mar 2017 21:46:18 +0200 Message-Id: <20170323194618.26548-9-andriy.shevchenko@linux.intel.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170323194618.26548-1-andriy.shevchenko@linux.intel.com> References: <20170323194618.26548-1-andriy.shevchenko@linux.intel.com> Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This allows ACPI GPIO code to modify flags based on ACPI GpioIo() / GpioInt() resources. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 50 +++++++++++++++++++++++++++++++++++++++++++-- drivers/gpio/gpiolib.c | 8 ++++++-- drivers/gpio/gpiolib.h | 15 ++++++++++++-- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index d4de84670c5b..5506b0736aba 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -442,6 +442,34 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) } } +int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + int ret = 0; + + /* + * Check if the BIOS has IoRestriction with explicitly set direction + * and update @flags accordingly. Otherwise use whatever caller asked + * for. + */ + if (update & GPIOD_FLAGS_BIT_DIR_SET) { + enum gpiod_flags diff = *flags ^ update; + + /* + * Check if caller supplied incompatible GPIO initialization + * flags. + * + * Return %-EINVAL to notify that firmware has different + * settings and we are going to use them. + */ + if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || + ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) + ret = -EINVAL; + *flags = update; + } + return ret; +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -479,8 +507,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH */ if (lookup->info.gpioint) { + lookup->info.flags = GPIOD_IN; lookup->info.polarity = agpio->polarity; lookup->info.triggering = agpio->triggering; + } else { + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); } } @@ -607,13 +638,14 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; + int err; int i; /* Try first from _DSD */ @@ -642,7 +674,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, } if (info.gpioint && - (flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH)) { + (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); return ERR_PTR(-ENOENT); } @@ -650,6 +682,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, if (info.polarity == GPIO_ACTIVE_LOW) *lookupflags |= GPIO_ACTIVE_LOW; + err = acpi_gpio_update_gpiod_flags(dflags, info.flags); + if (err) + dev_dbg(dev, "Override GPIO initialization flags\n"); + return desc; } @@ -703,12 +739,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, * used to translate from the GPIO offset in the resource to the Linux IRQ * number. * + * The function is idempotent, though each time it runs it will configure GPIO + * pin direction according to the flags in GpioInt resource. + * * Return: Linux IRQ number (>%0) on success, negative errno on failure. */ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) { int idx, i; unsigned int irq_flags; + int ret; for (i = 0, idx = 0; idx <= index; i++) { struct acpi_gpio_info info; @@ -721,6 +761,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) return PTR_ERR(desc); if (info.gpioint && idx++ == index) { + char label[32]; int irq; if (IS_ERR(desc)) @@ -730,6 +771,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (irq < 0) return irq; + snprintf(label, sizeof(label), "GpioInt() %d", index); + ret = gpiod_configure_flags(desc, label, 0, info.flags); + if (ret < 0) + return ret; + irq_flags = acpi_dev_get_irq_type(info.triggering, info.polarity); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3752785d31ab..6110fd62277a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3272,7 +3272,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, desc = of_find_gpio(dev, con_id, idx, &lookupflags); } else if (ACPI_COMPANION(dev)) { dev_dbg(dev, "using ACPI for GPIO lookup\n"); - desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags); + desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags); } } @@ -3351,8 +3351,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct acpi_gpio_info info; desc = acpi_node_get_gpiod(fwnode, propname, index, &info); - if (!IS_ERR(desc)) + if (!IS_ERR(desc)) { active_low = info.polarity == GPIO_ACTIVE_LOW; + ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags); + if (ret) + pr_debug("Override GPIO initialization flags\n"); + } } if (IS_ERR(desc)) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e36a0bdc7740..cff398cbb545 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -75,11 +75,13 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information + * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI */ struct acpi_gpio_info { + enum gpiod_flags flags; bool gpioint; int polarity; int triggering; @@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); +int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, + enum gpiod_flags update); + struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags); struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, @@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } +static inline int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + return 0; +} + static inline struct gpio_desc * acpi_find_gpio(struct device *dev, const char *con_id, - unsigned int idx, enum gpiod_flags flags, + unsigned int idx, enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { return ERR_PTR(-ENOENT);