From patchwork Mon Feb 29 13:46:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robin Murphy X-Patchwork-Id: 8454501 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 35FEDC0553 for ; Mon, 29 Feb 2016 13:49:47 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1789620220 for ; Mon, 29 Feb 2016 13:49:46 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CCC6B20109 for ; Mon, 29 Feb 2016 13:49:41 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1aaOB9-00016f-CC; Mon, 29 Feb 2016 13:48:03 +0000 Received: from foss.arm.com ([217.140.101.70]) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1aaOAK-0000dW-6Y for linux-arm-kernel@lists.infradead.org; Mon, 29 Feb 2016 13:47:14 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 7C6CE3A8; Mon, 29 Feb 2016 05:45:58 -0800 (PST) Received: from e104324-lin.cambridge.arm.com (e104324-lin.cambridge.arm.com [10.1.205.42]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 0ED483F213; Mon, 29 Feb 2016 05:46:50 -0800 (PST) From: Robin Murphy To: iommu@lists.linux-foundation.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH 02/12] of/irq: Break out msi-map lookup (again) Date: Mon, 29 Feb 2016 13:46:11 +0000 Message-Id: X-Mailer: git-send-email 2.7.2.333.g70bd996.dirty In-Reply-To: References: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160229_054712_386946_303EBE06 X-CRM114-Status: GOOD ( 18.25 ) X-Spam-Score: -6.9 (------) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Thomas.Lendacky@amd.com, anup.patel@broadcom.com, David Daney , Marc Zyngier , thunder.leizhen@huawei.com, will.deacon@arm.com, stuart.yoder@nxp.com, Rob Herring , Suravee.Suthikulpanit@amd.com, tchalamarla@caviumnetworks.com, Frank Rowand MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The PCI msi-map code is already doing double-duty translating IDs and retrieving MSI parents, which unsurprisingly is the same functionality we need for the identically-formatted PCI iommu-map property. Drag the core parsing routine up yet another layer into the general OF-PCI code, and further generalise it for either kind of lookup in either flavour of map property. CC: Rob Herring CC: Frank Rowand CC: Marc Zyngier CC: David Daney Signed-off-by: Robin Murphy --- drivers/of/irq.c | 70 ++------------------------------- drivers/of/of_pci.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_pci.h | 8 ++++ 3 files changed, 114 insertions(+), 66 deletions(-) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index e7bfc17..0c9118d 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -586,13 +587,7 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np, u32 rid_in) { struct device *parent_dev; - struct device_node *msi_controller_node; - struct device_node *msi_np = *np; - u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle; - int msi_map_len; - bool matched; u32 rid_out = rid_in; - const __be32 *msi_map = NULL; /* * Walk up the device parent links looking for one with a @@ -602,71 +597,14 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np, if (!parent_dev->of_node) continue; - msi_map = of_get_property(parent_dev->of_node, - "msi-map", &msi_map_len); - if (!msi_map) + if (!of_property_read_bool(parent_dev->of_node, "msi-map")) continue; - if (msi_map_len % (4 * sizeof(__be32))) { - dev_err(parent_dev, "Error: Bad msi-map length: %d\n", - msi_map_len); - return rid_out; - } /* We have a good parent_dev and msi_map, let's use them. */ + of_pci_map_rid(parent_dev->of_node, "msi-map", rid_in, np, + &rid_out); break; } - if (!msi_map) - return rid_out; - - /* The default is to select all bits. */ - map_mask = 0xffffffff; - - /* - * Can be overridden by "msi-map-mask" property. If - * of_property_read_u32() fails, the default is used. - */ - of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask); - - masked_rid = map_mask & rid_in; - matched = false; - while (!matched && msi_map_len >= 4 * sizeof(__be32)) { - rid_base = be32_to_cpup(msi_map + 0); - phandle = be32_to_cpup(msi_map + 1); - msi_base = be32_to_cpup(msi_map + 2); - rid_len = be32_to_cpup(msi_map + 3); - - if (rid_base & ~map_mask) { - dev_err(parent_dev, - "Invalid msi-map translation - msi-map-mask (0x%x) ignores rid-base (0x%x)\n", - map_mask, rid_base); - return rid_out; - } - - msi_controller_node = of_find_node_by_phandle(phandle); - - matched = (masked_rid >= rid_base && - masked_rid < rid_base + rid_len); - if (msi_np) - matched &= msi_np == msi_controller_node; - - if (matched && !msi_np) { - *np = msi_np = msi_controller_node; - break; - } - - of_node_put(msi_controller_node); - msi_map_len -= 4 * sizeof(__be32); - msi_map += 4; - } - if (!matched) - return rid_out; - - rid_out = masked_rid - rid_base + msi_base; - dev_dbg(dev, - "msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n", - dev_name(parent_dev), map_mask, rid_base, msi_base, - rid_len, rid_in, rid_out); - return rid_out; } diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index b1449f7..15c4a64b 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -307,3 +307,105 @@ struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node) EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node); #endif /* CONFIG_PCI_MSI */ + +#define MASK_NAME_LEN 32 /* Safely longer than "iommu-map-mask" */ + +/** + * of_pci_map_rid - Translate a requester ID through a downstream mapping. + * @np: root complex device node. + * @map_name: property name of the map to use. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Given a PCI requester ID, look up the appropriate implementation-defined + * platform ID and/or the target device which receives transactions on that + * ID, as per the "iommu-map" and "msi-map" bindings. @target or @id_out may + * be NULL if not required. If @target points to a device node pointer, only + * entries targeting that node will be matched; if it points to a NULL + * value, it will receive the device node for the first matching target entry, + * with a reference held. + * + * Return: 0 on success or a standard error code on failure. + */ +int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in, + struct device_node **target, u32 *rid_out) +{ + u32 map_mask, masked_rid; + int map_len; + const __be32 *map = NULL; + char mask_name[MASK_NAME_LEN]; + + if (!np || !map_name || (!target && !rid_out)) + return -EINVAL; + + map = of_get_property(np, map_name, &map_len); + if (!map) { + if (target) + return -ENODEV; + /* Otherwise, no map implies no translation */ + *rid_out = rid_in; + return 0; + } + + if (!map_len || map_len % (4 * sizeof(*map))) { + pr_err("%s: Error: Bad %s length: %d\n", np->full_name, + map_name, map_len); + return -EINVAL; + } + + /* The default is to select all bits. */ + map_mask = 0xffffffff; + + /* + * Can be overridden by "{iommu,msi}-map-mask" property. + * If of_property_read_u32() fails, the default is used. + */ + snprintf(mask_name, MASK_NAME_LEN, "%s-mask", map_name); + of_property_read_u32(np, mask_name, &map_mask); + + masked_rid = map_mask & rid_in; + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + struct device_node *phandle_node; + u32 rid_base = be32_to_cpup(map + 0); + u32 phandle = be32_to_cpup(map + 1); + u32 out_base = be32_to_cpup(map + 2); + u32 rid_len = be32_to_cpup(map + 3); + + if (rid_base & ~map_mask) { + pr_err("%s: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n", + np->full_name, map_name, map_name, + map_mask, rid_base); + return -EINVAL; + } + + if (masked_rid < rid_base || masked_rid >= rid_base + rid_len) + continue; + + phandle_node = of_find_node_by_phandle(phandle); + if (!phandle_node) + return -ENODEV; + + if (target) { + if (*target) + of_node_put(phandle_node); + else + *target = phandle_node; + + if (*target != phandle_node) + continue; + } + + if (rid_out) + *rid_out = masked_rid - rid_base + out_base; + + pr_debug("%s: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n", + np->full_name, map_name, map_mask, rid_base, out_base, + rid_len, rid_in, *rid_out); + return 0; + } + + pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n", + np->full_name, map_name, rid_in, + target && *target ? (*target)->full_name : "any target"); + return -EINVAL; +} diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index f6e9e85..070f883 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -17,6 +17,8 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); int of_pci_parse_bus_range(struct device_node *node, struct resource *res); int of_get_pci_domain_nr(struct device_node *node); void of_pci_check_probe_only(void); +int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in, + struct device_node **target, u32 *rid_out); #else static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) { @@ -52,6 +54,12 @@ of_get_pci_domain_nr(struct device_node *node) return -1; } +static inline int of_pci_map_rid(struct device_node *np, const char *map_name, + u32 rid_in, struct device_node **target, u32 *rid_out) +{ + return -EINVAL; +} + static inline void of_pci_check_probe_only(void) { } #endif