From patchwork Thu Sep 7 15:33:28 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timur Tabi X-Patchwork-Id: 811077 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=codeaurora.org header.i=@codeaurora.org header.b="kNlpOMFd"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=codeaurora.org header.i=@codeaurora.org header.b="L9VYmaDT"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3xp4HR3TS6z9t2r for ; Fri, 8 Sep 2017 01:33:55 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754514AbdIGPdy (ORCPT ); Thu, 7 Sep 2017 11:33:54 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:47490 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754858AbdIGPdx (ORCPT ); Thu, 7 Sep 2017 11:33:53 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 0A848611A5; Thu, 7 Sep 2017 15:33:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1504798433; bh=oesfOqOwzo6xKcvVlcYc2Bf4qD/Q0M4gqAf6sA4aErw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kNlpOMFdVDotUiI1wrSEJmJFgTlBW0OfegbSRAcFmyvplOmMLePnoPWBYv52NBfW6 /TWBpv3tCMC3JOB1iGYPi9Yi6tXkO5HokOFN6+qdT52XOcvZuhy67U3tRwaW58MBWB xjBSouKKuM7BNQF1SO8QPOZJXFd0TLeKLQ5AFdP8= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED, T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from timur-ubuntu.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: timur@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 1A97560CA5; Thu, 7 Sep 2017 15:33:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1504798429; bh=oesfOqOwzo6xKcvVlcYc2Bf4qD/Q0M4gqAf6sA4aErw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L9VYmaDTnxV6PlYXYk/pcbCwZwUfhVpEdUjPW380jK7pVOb2Hcbb8/AUQK1M6QaEK 4caZABpt59UyRfrDHC8pNHoO7mjrdg1Or8gWKJjRxGWWFneDnrv5I3MlHb7w02aEEx 45yQ/Z4nFRjmxku82Zuakse5TswrG2rYkck/JiCE= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 1A97560CA5 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=timur@codeaurora.org From: Timur Tabi To: Linus Walleij , andy.gross@linaro.org, david.brown@linaro.org, anjiandi@codeaurora.org, Bjorn Andersson , linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org Cc: timur@codeaurora.org Subject: [PATCH 1/2] [v5] pinctrl: qcom: disable GPIO groups with no pins Date: Thu, 7 Sep 2017 10:33:28 -0500 Message-Id: <1504798409-32041-2-git-send-email-timur@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1504798409-32041-1-git-send-email-timur@codeaurora.org> References: <1504798409-32041-1-git-send-email-timur@codeaurora.org> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org pinctrl-msm only accepts an array of GPIOs from 0 to n-1, and it expects each group to support have only one pin (npins == 1). We can support "sparse" GPIO maps if we allow for some groups to have zero pins (npins == 0). These pins are "hidden" from the rest of the driver and gpiolib. A new boolean 'sparse' indicates whether the GPIO map is sparse. If any GPIO has an 'npins' value of 0, then 'sparse' must be set to True. Most access to unavailable GPIOs can be blocked via the gpio_chip.request function. The one exception is when gpiochip_add_data() scans all of the GPIOs without "requesting" them. To cover this case, msm_gpio_get_direction() separately checks if the GPIO is available. Signed-off-by: Timur Tabi --- drivers/pinctrl/qcom/pinctrl-msm.c | 110 +++++++++++++++++++++++++++++++++++-- drivers/pinctrl/qcom/pinctrl-msm.h | 2 + 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index ff491da64dab..ca4ae3d76eb4 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -443,6 +443,14 @@ static int msm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) g = &pctrl->soc->groups[offset]; + /* + * During initialization, gpiolib may query all GPIOs for their + * initial direction, regardless if they exist, so block access + * to those that are unavailable. + */ + if (!g->npins) + return -ENODEV; + val = readl(pctrl->regs + g->ctl_reg); /* 0 = output, 1 = input */ @@ -507,6 +515,11 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, }; g = &pctrl->soc->groups[offset]; + + /* If the GPIO group has no pins, then don't show it. */ + if (!g->npins) + return; + ctl_reg = readl(pctrl->regs + g->ctl_reg); is_out = !!(ctl_reg & BIT(g->oe_bit)); @@ -516,7 +529,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); seq_printf(s, " %dmA", msm_regval_to_drive(drive)); - seq_printf(s, " %s", pulls[pull]); + seq_printf(s, " %s\n", pulls[pull]); } static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -524,23 +537,36 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned gpio = chip->base; unsigned i; - for (i = 0; i < chip->ngpio; i++, gpio++) { + for (i = 0; i < chip->ngpio; i++, gpio++) msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); - seq_puts(s, "\n"); - } } #else #define msm_gpio_dbg_show NULL #endif +/* + * If the requested GPIO has no pins, then treat it as unavailable. + * Otherwise, call the standard request function. + */ +static int msm_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + const struct msm_pingroup *g = &pctrl->soc->groups[offset]; + + if (!g->npins) + return -ENODEV; + + return gpiochip_generic_request(chip, offset); +} + static const struct gpio_chip msm_gpio_template = { .direction_input = msm_gpio_direction_input, .direction_output = msm_gpio_direction_output, .get_direction = msm_gpio_get_direction, .get = msm_gpio_get, .set = msm_gpio_set, - .request = gpiochip_generic_request, + .request = msm_gpio_request, .free = gpiochip_generic_free, .dbg_show = msm_gpio_dbg_show, }; @@ -808,11 +834,57 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +/** + * msm_gpio_get_next_range() - find next range of consecutive gpios + * @pctrl: msm_pinctrl object + * @pstart: pointer to index of next starting position + * + * In a sparse GPIO map, available GPIOs are typically collected into blocks. + * Given a starting index into the groups[] array, this function will scan all + * the gpios until it finds the next block of available GPIOs. + * + * If groups[*pstart] already points to an available GPIO, then assume that + * it is the start of a block. + * + * The caller is responsible for moving *pstart to after the end of the + * previous block so that it this function can find the next block. + * + * Return: The count of the block starting at @pstart, or 0 if there are no + * more blocks. + */ +static unsigned int msm_gpio_get_next_range(struct msm_pinctrl *pctrl, + unsigned int *pstart) +{ + const struct msm_pingroup *groups = pctrl->soc->groups; + unsigned int ngpio = pctrl->soc->ngpios; + unsigned int count = 0; + unsigned int start = *pstart; + + /* Find the first available GPIO */ + while (!groups[start].npins) + if (++start == ngpio) + /* None found, so just exit */ + return 0; + + *pstart = start; + + /* Count the number of those GPIOs in a row */ + while (groups[start].npins) { + count++; + if (++start == ngpio) + break; + } + + return count; +} + static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; int ret; unsigned ngpio = pctrl->soc->ngpios; + const struct msm_pingroup *groups = pctrl->soc->groups; + unsigned int i, start = 0, count; if (WARN_ON(ngpio > MAX_NR_GPIO)) return -EINVAL; @@ -825,13 +897,39 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) chip->owner = THIS_MODULE; chip->of_node = pctrl->dev->of_node; + /* If the GPIO map is sparse, then we need to disable specific IRQs */ + chip->irq_need_valid_mask = pctrl->soc->sparse; + ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { dev_err(pctrl->dev, "Failed register gpiochip\n"); return ret; } - ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio); + /* + * If irq_need_valid_mask is true, then gpiochip_add_data() will + * initialize irq_valid_mask to all 1s. We need to clear all the + * GPIOs that are unavailable, and we need to find each block + * of consecutive available GPIOs are add them as pin ranges. + */ + if (chip->irq_need_valid_mask) { + for (i = 0; i < ngpio; i++) + if (!groups[i].npins) + clear_bit(i, pctrl->chip.irq_valid_mask); + + while ((count = msm_gpio_get_next_range(pctrl, &start))) { + ret = gpiochip_add_pin_range(&pctrl->chip, + dev_name(pctrl->dev), + start, start, count); + if (ret) + break; + start += count; + } + } else { + ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), + 0, 0, ngpio); + } + if (ret) { dev_err(pctrl->dev, "Failed to add pin range\n"); gpiochip_remove(&pctrl->chip); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 9b9feea540ff..70762bcb84cb 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -107,6 +107,7 @@ struct msm_pingroup { * @ngroups: The numbmer of entries in @groups. * @ngpio: The number of pingroups the driver should expose as GPIOs. * @pull_no_keeper: The SoC does not support keeper bias. + * @sparse: The GPIO map is sparse (some GPIOs have npins == 0) */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; @@ -117,6 +118,7 @@ struct msm_pinctrl_soc_data { unsigned ngroups; unsigned ngpios; bool pull_no_keeper; + bool sparse; }; int msm_pinctrl_probe(struct platform_device *pdev,