From patchwork Fri Aug 11 15:42:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 109904 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp1080553qge; Fri, 11 Aug 2017 08:43:12 -0700 (PDT) X-Received: by 10.98.89.22 with SMTP id n22mr16713523pfb.326.1502466192122; Fri, 11 Aug 2017 08:43:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502466192; cv=none; d=google.com; s=arc-20160816; b=r3PirYiQN/0hoQqffY7aDI2NCC0d1GcMz6l1O2xaNrjcJQmONR0wgnXNxuucpa1TII 8V2EG3F8sp1nxFUjpVC1aCeiwL8D4WrDIWwIr7OGJ91JAKd3lMqvRBkze7llF5S9IVmO n4ojopUsMxPTmIZ4I8pEe4M/WGykpLIbE9HU7ivQCv5yPjqw5K0PES963O2F96Qx7Usf zZNd0VOwBV696DHKCuf0GIw/veDCIOt9mcQdNgC+WRBEKbOHAUlKuWlo5xf5Lcaaa88F CmHm7+h6NubZnlihzIC5h77z8lhTYAhagQF5Vmh6l8s6dhrFlC6865HHRKVPapsDdHCP MR/g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=YOKjLWkBn9ODrACp8poGDTiim6PDv8/YEoUb/jG9CUA=; b=r8RxZpKmBuHLbsK8bz4vbUGwLM3fX4cnsKZgDmMX27SC+nXTR3A4938kGeJHP1SmUn QoktjVB0Q6N9yMzL+2q050G+0/bbbcjt0cVIntOWysQUo+e5jGiUVZGdfU/NHNQgAZhB RKWAopW822w6uUkCMMcXec4z2Sl5GRjMWq8Kon0XNHiECncKVF1vqLhW3z2HPyo/5AG7 TU+8rlqo7lPiQtGdCB8z6BhMDyeC/jUzXSF+iiLl9gQE+RFA0mL6vuAi79GgDuucl16v UDVpZeNlGEv9nDVLJFZK1eeFxDEaQcXqyAhyFToPxebzmAPj1Rh3fCxDuubu+Tic4OwB a6zw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=gRlAi9cQ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q75si675796pfq.132.2017.08.11.08.43.11; Fri, 11 Aug 2017 08:43:12 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=gRlAi9cQ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753471AbdHKPnI (ORCPT + 25 others); Fri, 11 Aug 2017 11:43:08 -0400 Received: from mail-pf0-f174.google.com ([209.85.192.174]:36416 "EHLO mail-pf0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753267AbdHKPml (ORCPT ); Fri, 11 Aug 2017 11:42:41 -0400 Received: by mail-pf0-f174.google.com with SMTP id c28so17263288pfe.3 for ; Fri, 11 Aug 2017 08:42:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YOKjLWkBn9ODrACp8poGDTiim6PDv8/YEoUb/jG9CUA=; b=gRlAi9cQI+2AYD7Tv0O+lumqB1GcQZaCFhqwbekcoXgJB2jDI+zk5glR/dHwNFyfxC yO0nPGSG+HaqhI5MrTFnyDTPdbYgybYZT+nx7a/jsd3rf7AqtZDZ7j2OWeTgsS49/lqN VtQ7k4ZyOA6aoqmKz2ke3XVpRfHhkdFrLHAIE= 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; bh=YOKjLWkBn9ODrACp8poGDTiim6PDv8/YEoUb/jG9CUA=; b=jAFw5QJQUdoJsSW32lEQ7rnrJlSfpYvqECcHlMJK6WcWrL+e2XIXoGi0RLH7pdnblL l4t6noo1lsbcbKlfbJTmRiyHh2Zpc79wUNyM/2UPTY9KpONapX31v5Q+bYmO1rMlyprl gH6VfnoWC+3bAxOVDEx+xoyR4VwYgrpBrBTeXXVeGL6L/dKKd3bGGUJB+EYMb6lQQHVW IcjRGWcNwjk4WevTVCU0eqIGrloOz1Ada1z4AXamAtvztEHsQKiCncKBrsJ7CuG/41vg Osxqx2XD5T5EMcEIDCQ+TbULMWUuMvFneq8wWjOpepPAJghiIYY0GK2qaBKl/mmBr7X+ 1TFw== X-Gm-Message-State: AHYfb5gTQqhq84PiLwhcMrcwXobOFCV6X/us5SS5PjhZTc2w2Vw4Icp3 Q+cIyKzHjkLC1MhM X-Received: by 10.99.167.11 with SMTP id d11mr16252895pgf.182.1502466160632; Fri, 11 Aug 2017 08:42:40 -0700 (PDT) Received: from localhost.localdomain (i-global254.qualcomm.com. [199.106.103.254]) by smtp.gmail.com with ESMTPSA id a63sm2351071pfc.165.2017.08.11.08.42.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 08:42:40 -0700 (PDT) From: Stephen Boyd To: Rob Herring , Frank Rowand Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Russell King - ARM Linux , devicetree-spec@vger.kernel.org, Pantelis Antoniou , Linus Walleij , Mark Brown Subject: [PATCH v4 2/4] of: Support parsing phandle argument lists through a nexus node Date: Fri, 11 Aug 2017 08:42:34 -0700 Message-Id: <20170811154236.12891-3-stephen.boyd@linaro.org> X-Mailer: git-send-email 2.14.GIT In-Reply-To: <20170811154236.12891-1-stephen.boyd@linaro.org> References: <20170811154236.12891-1-stephen.boyd@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Platforms like 96boards have a standardized connector/expansion slot that exposes signals like GPIOs to expansion boards in an SoC agnostic way. We'd like the DT overlays for the expansion boards to be written once without knowledge of the SoC on the other side of the connector. This avoids the unscalable combinatorial explosion of a different DT overlay for each expansion board and SoC pair. We need a way to describe the GPIOs routed through the connector in an SoC agnostic way. Let's introduce nexus property parsing into the OF core to do this. This is largely based on the interrupt nexus support we already have. This allows us to remap a phandle list in a consumer node (e.g. reset-gpios) through a connector in a generic way (e.g. via gpio-map). Do this in a generic routine so that we can remap any sort of variable length phandle list. Taking GPIOs as an example, the connector would be a GPIO nexus, supporting the remapping of a GPIO specifier space to multiple GPIO providers on the SoC. DT would look as shown below, where 'soc_gpio1' and 'soc_gpio2' are inside the SoC, 'connector' is an expansion port where boards can be plugged in, and 'expansion_device' is a device on the expansion board. soc { soc_gpio1: gpio-controller1 { #gpio-cells = <2>; }; soc_gpio2: gpio-controller2 { #gpio-cells = <2>; }; }; connector: connector { #gpio-cells = <2>; gpio-map = <0 0 &soc_gpio1 1 0>, <1 0 &soc_gpio2 4 0>, <2 0 &soc_gpio1 3 0>, <3 0 &soc_gpio2 2 0>; gpio-map-mask = <0xf 0x0>; gpio-map-pass-thru = <0x0 0x1> }; expansion_device { reset-gpios = <&connector 2 GPIO_ACTIVE_LOW>; }; The GPIO core would use of_parse_phandle_with_args_map() instead of of_parse_phandle_with_args() and arrive at the same type of result, a phandle and argument list. The difference is that the phandle and arguments will be remapped through the nexus node to the underlying SoC GPIO controller node. In the example above, we would remap 'reset-gpios' from <&connector 2 GPIO_ACTIVE_LOW> to <&soc_gpio1 3 GPIO_ACTIVE_LOW>. Cc: Pantelis Antoniou Cc: Linus Walleij Cc: Mark Brown Signed-off-by: Stephen Boyd --- drivers/of/base.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 12 ++++ 2 files changed, 196 insertions(+) -- 2.7.4 diff --git a/drivers/of/base.c b/drivers/of/base.c index 686628d1dfa6..60f15bf56534 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1366,6 +1366,190 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na EXPORT_SYMBOL(of_parse_phandle_with_args); /** + * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @stem_name: stem of property names that specify phandles' arguments count + * @index: index of a phandle to parse out + * @out_args: optional pointer to output arguments structure (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_args, on error returns appropriate errno + * value. The difference between this function and of_parse_phandle_with_args() + * is that this API remaps a phandle if the node the phandle points to has + * a <@stem_name>-map property. + * + * Caller is responsible to call of_node_put() on the returned out_args->np + * pointer. + * + * Example: + * + * phandle1: node1 { + * #list-cells = <2>; + * } + * + * phandle2: node2 { + * #list-cells = <1>; + * } + * + * phandle3: node3 { + * #list-cells = <1>; + * list-map = <0 &phandle2 3>, + * <1 &phandle2 2>, + * <2 &phandle1 5 1>; + * list-map-mask = <0x3>; + * }; + * + * node4 { + * list = <&phandle1 1 2 &phandle3 0>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandle_with_args(node4, "list", "list", 1, &args); + */ +int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, + const char *stem_name, + int index, struct of_phandle_args *out_args) +{ + char *cells_name, *map_name = NULL, *mask_name = NULL; + char *pass_name = NULL; + struct device_node *cur, *new = NULL; + const __be32 *map, *mask, *pass; + static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; + static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 }; + __be32 initial_match_array[MAX_PHANDLE_ARGS]; + const __be32 *match_array = initial_match_array; + int i, ret, map_len, match; + u32 list_size, new_size; + + if (index < 0) + return -EINVAL; + + cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); + if (!cells_name) + return -ENOMEM; + + ret = -ENOMEM; + map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name); + if (!map_name) + goto free; + + mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); + if (!mask_name) + goto free; + + pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); + if (!pass_name) + goto free; + + ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index, + out_args); + if (ret) + goto free; + + /* Get the #-cells property */ + cur = out_args->np; + ret = of_property_read_u32(cur, cells_name, &list_size); + if (ret < 0) + goto put; + + /* Precalculate the match array - this simplifies match loop */ + for (i = 0; i < list_size; i++) + initial_match_array[i] = cpu_to_be32(out_args->args[i]); + + ret = -EINVAL; + while (cur) { + /* Get the -map property */ + map = of_get_property(cur, map_name, &map_len); + if (!map) { + ret = 0; + goto free; + } + map_len /= sizeof(u32); + + /* Get the -map-mask property (optional) */ + mask = of_get_property(cur, mask_name, NULL); + if (!mask) + mask = dummy_mask; + /* Iterate through -map property */ + match = 0; + while (map_len > (list_size + 1) && !match) { + /* Compare specifiers */ + match = 1; + for (i = 0; i < list_size; i++, map_len--) + match &= !((match_array[i] ^ *map++) & mask[i]); + + of_node_put(new); + new = of_find_node_by_phandle(be32_to_cpup(map)); + map++; + map_len--; + + /* Check if not found */ + if (!new) + goto put; + + if (!of_device_is_available(new)) + match = 0; + + ret = of_property_read_u32(new, cells_name, &new_size); + if (ret) + goto put; + + /* Check for malformed properties */ + if (WARN_ON(new_size > MAX_PHANDLE_ARGS)) + goto put; + if (map_len < new_size) + goto put; + + /* Move forward by new node's #-cells amount */ + map += new_size; + map_len -= new_size; + } + if (!match) + goto put; + + /* Get the -map-pass-thru property (optional) */ + pass = of_get_property(cur, pass_name, NULL); + if (!pass) + pass = dummy_pass; + + /* + * Successfully parsed a -map translation; copy new + * specifier into the out_args structure, keeping the + * bits specified in -map-pass-thru. + */ + match_array = map - new_size; + for (i = 0; i < new_size; i++) { + __be32 val = *(map - new_size + i); + + if (i < list_size) { + val &= ~pass[i]; + val |= cpu_to_be32(out_args->args[i]) & pass[i]; + } + + out_args->args[i] = be32_to_cpu(val); + } + out_args->args_count = list_size = new_size; + /* Iterate again with new provider */ + out_args->np = new; + of_node_put(cur); + cur = new; + } +put: + of_node_put(cur); + of_node_put(new); +free: + kfree(mask_name); + kfree(map_name); + kfree(cells_name); + kfree(pass_name); + + return ret; +} +EXPORT_SYMBOL(of_parse_phandle_with_args_map); + +/** * of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list * @np: pointer to a device tree node containing a list * @list_name: property name that contains a list diff --git a/include/linux/of.h b/include/linux/of.h index 23bf46ee8686..92fd032b76c7 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -362,6 +362,9 @@ extern struct device_node *of_parse_phandle(const struct device_node *np, extern int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args); +extern int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, const char *stem_name, int index, + struct of_phandle_args *out_args); extern int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args); @@ -764,6 +767,15 @@ static inline int of_parse_phandle_with_args(const struct device_node *np, return -ENOSYS; } +static inline int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, + const char *stem_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + static inline int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args)