From patchwork Tue Jan 8 07:18:53 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Courbot X-Patchwork-Id: 1944401 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 318663FED4 for ; Tue, 8 Jan 2013 07:22:18 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TsTTD-0000MD-Ew; Tue, 08 Jan 2013 07:19:35 +0000 Received: from hqemgate03.nvidia.com ([216.228.121.140]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TsTSz-0000JK-MQ for linux-arm-kernel@lists.infradead.org; Tue, 08 Jan 2013 07:19:24 +0000 Received: from hqnvupgp06.nvidia.com (Not Verified[216.228.121.13]) by hqemgate03.nvidia.com id ; Mon, 07 Jan 2013 23:23:18 -0800 Received: from hqemhub01.nvidia.com ([172.17.108.22]) by hqnvupgp06.nvidia.com (PGP Universal service); Mon, 07 Jan 2013 23:16:33 -0800 X-PGP-Universal: processed; by hqnvupgp06.nvidia.com on Mon, 07 Jan 2013 23:16:33 -0800 Received: from percival.nvidia.com (172.20.144.16) by hqemhub01.nvidia.com (172.20.150.30) with Microsoft SMTP Server (TLS) id 8.3.279.1; Mon, 7 Jan 2013 23:19:19 -0800 From: Alexandre Courbot To: Grant Likely , Linus Walleij , Arnd Bergmann Subject: [PATCH 2/4] gpiolib: add gpiod_get and gpiod_put functions Date: Tue, 8 Jan 2013 16:18:53 +0900 Message-ID: <1357629535-26033-3-git-send-email-acourbot@nvidia.com> X-Mailer: git-send-email 1.8.1 In-Reply-To: <1357629535-26033-1-git-send-email-acourbot@nvidia.com> References: <1357629535-26033-1-git-send-email-acourbot@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130108_021922_096278_254DF355 X-CRM114-Status: GOOD ( 18.11 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [216.228.121.140 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record -0.7 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: linux-arch@vger.kernel.org, Alexandre Courbot , devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, Alexandre Courbot , linux-arm-kernel@lists.infradead.org, Guenter Roeck X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Adds new GPIO allocation functions that work with the opaque descriptor interface. Signed-off-by: Alexandre Courbot --- drivers/gpio/gpiolib.c | 164 ++++++++++++++++++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 8 +++ include/linux/gpio/driver.h | 21 ++++++ 3 files changed, 193 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d04b90b..06ffadb 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,11 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +/* protects both gpio_lookup_list and gpio_chips */ +static DEFINE_MUTEX(gpio_lookup_lock); +static LIST_HEAD(gpio_lookup_list); +static LIST_HEAD(gpio_chips); + #ifdef CONFIG_GPIO_SYSFS static DEFINE_IDR(dirent_idr); #endif @@ -1162,6 +1168,11 @@ int gpiochip_add(struct gpio_chip *chip) of_gpiochip_add(chip); + INIT_LIST_HEAD(&chip->list); + mutex_lock(&gpio_lookup_lock); + list_add(&chip->list, &gpio_chips); + mutex_unlock(&gpio_lookup_lock); + unlock: spin_unlock_irqrestore(&gpio_lock, flags); @@ -1200,6 +1211,10 @@ int gpiochip_remove(struct gpio_chip *chip) spin_lock_irqsave(&gpio_lock, flags); + mutex_lock(&gpio_lookup_lock); + list_del_init(&chip->list); + mutex_unlock(&gpio_lookup_lock); + gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); @@ -1924,6 +1939,155 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) } EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); + +/** + * gpioh_add_table() - register GPIO device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void gpiod_add_table(struct gpiod_lookup *table, size_t size) +{ + mutex_lock(&gpio_lookup_lock); + + while (size--) { + list_add_tail(&table->list, &gpio_lookup_list); + table++; + } + + mutex_unlock(&gpio_lookup_lock); +} + +/* + * Caller must have a acquired gpio_lookup_lock + */ +static struct gpio_chip *find_chip_by_name(const char *name) +{ + struct gpio_chip *chip = NULL; + + list_for_each_entry(chip, &gpio_lookup_list, list) { + if (chip->label == NULL) + continue; + if (!strcmp(chip->label, name)) + break; + } + + return chip; +} + +#ifdef CONFIG_OF +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id) +{ + char prop_name[32]; /* 32 is max size of property name */ + + if (con_id) + snprintf(prop_name, 32, "%s-gpios", con_id); + else + snprintf(prop_name, 32, "gpios"); + + return of_get_named_gpiod_flags(dev->of_node, prop_name, 0, NULL); +} +#else +static struct device_node *of_find_gpio(struct device *dev, const char *id) +{ + return NULL; +} +#endif + +static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id) +{ + const char *dev_id = dev ? dev_name(dev) : NULL; + struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned int match, best = 0; + struct gpiod_lookup *p; + + mutex_lock(&gpio_lookup_lock); + + list_for_each_entry(p, &gpio_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (match > best) { + struct gpio_chip *chip; + + chip = find_chip_by_name(p->chip_label); + if (!chip) { + dev_warn(dev, "cannot find GPIO chip %s\n", + p->chip_label); + continue; + } + + if (chip->ngpio >= p->chip_hwnum) { + dev_warn(dev, "GPIO chip %s has %d GPIOs\n", + chip->label, chip->ngpio); + continue; + } + + desc = gpio_to_desc(chip->base + p->chip_hwnum); + + if (match != 3) + best = match; + else + break; + } + } + + mutex_unlock(&gpio_lookup_lock); + + return desc; +} + +/** + * + */ +struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) +{ + struct gpio_desc *desc; + int status; + + dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); + + /* Using device tree? */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { + dev_dbg(dev, "using device tree for GPIO lookup\n"); + desc = of_find_gpio(dev, con_id); + } else { + dev_dbg(dev, "using lookup tables for GPIO lookup"); + desc = gpiod_find(dev, con_id); + } + + if (IS_ERR(desc)) { + dev_warn(dev, "lookup for GPIO %s failed\n", con_id); + return desc; + } + + status = gpiod_request(desc, con_id); + + if (status < 0) + return ERR_PTR(status); + + return desc; +} +EXPORT_SYMBOL_GPL(gpiod_get); + +void gpiod_put(struct gpio_desc *desc) +{ + gpiod_free(desc); +} +EXPORT_SYMBOL_GPL(gpiod_put); + #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 0ce96cf..2f30761 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -15,6 +15,14 @@ struct gpio_chip; */ struct gpio_desc; +struct gpio_desc *__must_check gpiod_get(struct device *dev, + const char *con_id); +void gpiod_put(struct gpio_desc *desc); + +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id); +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); + struct gpio_desc *gpio_to_desc(unsigned gpio); int desc_to_gpio(const struct gpio_desc *desc); struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index d42f941..d43d671 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -4,12 +4,14 @@ #ifdef CONFIG_GPIOLIB #include +#include /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics * @dev: optional device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs + * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -55,6 +57,7 @@ struct gpio_chip { const char *label; struct device *dev; struct module *owner; + struct list_head list; int (*request)(struct gpio_chip *chip, unsigned offset); @@ -107,6 +110,24 @@ struct gpio_chip { #endif }; +struct gpiod_lookup { + struct list_head list; + const char *chip_label; + u16 chip_hwnum; + const char *dev_id; + const char *con_id; +}; + +#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _dev_id, _con_id) \ +{ \ + .chip_label = _chip_label, \ + .chip_hwnum = _chip_hwnum, \ + .dev_id = _dev_id, \ + .con_id = _con_id, \ +} + +void gpiod_add_table(struct gpiod_lookup *table, size_t size); + #endif /* CONFIG_GPIOLIB */ #endif