devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Thierry Reding <thierry.reding@gmail.com>
To: Rob Herring <robh+dt@kernel.org>, Joerg Roedel <joro@8bytes.org>
Cc: Will Deacon <will@kernel.org>,
	Robin Murphy <robin.murphy@arm.com>,
	Nicolin Chen <nicolinc@nvidia.com>,
	Krishna Reddy <vdumpa@nvidia.com>,
	Ashish Mhetre <amhetre@nvidia.com>,
	Dmitry Osipenko <dmitry.osipenko@collabora.com>,
	Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>,
	Janne Grunau <j@jannau.net>, Sameer Pujar <spujar@nvidia.com>,
	Lucas Stach <l.stach@pengutronix.de>,
	devicetree@vger.kernel.org, iommu@lists.linux-foundation.org,
	linux-tegra@vger.kernel.org, asahi@lists.linux.dev
Subject: [PATCH v11 1/5] of: Introduce support for #dma-{address,size}-cells
Date: Fri, 11 Nov 2022 17:18:02 +0100	[thread overview]
Message-ID: <20221111161806.630527-2-thierry.reding@gmail.com> (raw)
In-Reply-To: <20221111161806.630527-1-thierry.reding@gmail.com>

From: Thierry Reding <treding@nvidia.com>

The control and DMA busses of a system can have differing addressing
requirements. One common case of this is SoCs with CPUs that can address
more than 32 bits of system memory, but for simplicity want to describe
the control busses using #address-cells = <1> and #size-cells = <1>. In
this case, DMA address translation will fail because > 32-bit addresses
will be truncated to 32-bit addresses.

Support for the new #dma-address-cells and #dma-size-cells properties is
introduced in this patch. If these are encountered, they will be used
instead of the #address-and #size-cells properties when translating DMA
addresses.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v11:
- new patch

 drivers/of/address.c       | 43 ++++++++++++-----------
 drivers/of/base.c          | 70 ++++++++++++++++++++++++++++++--------
 drivers/of/of_private.h    | 14 ++++++--
 include/linux/of.h         | 17 +++++++--
 include/linux/of_address.h |  2 +-
 5 files changed, 108 insertions(+), 38 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index c34ac33b7338..db59bc60226c 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -45,8 +45,8 @@ struct of_bus {
 	const char	*name;
 	const char	*addresses;
 	int		(*match)(struct device_node *parent);
-	void		(*count_cells)(struct device_node *child,
-				       int *addrc, int *sizec);
+	void		(*count_cells)(struct device_node *child, int *addrc, int *sizec,
+				       unsigned int flags);
 	u64		(*map)(__be32 *addr, const __be32 *range,
 				int na, int ns, int pna);
 	int		(*translate)(__be32 *addr, u64 offset, int na);
@@ -58,13 +58,13 @@ struct of_bus {
  * Default translator (generic bus)
  */
 
-static void of_bus_default_count_cells(struct device_node *dev,
-				       int *addrc, int *sizec)
+static void of_bus_default_count_cells(struct device_node *dev, int *addrc, int *sizec,
+				       unsigned int flags)
 {
 	if (addrc)
-		*addrc = of_n_addr_cells(dev);
+		*addrc = of_addr_cells(dev, flags);
 	if (sizec)
-		*sizec = of_n_size_cells(dev);
+		*sizec = of_size_cells(dev, flags);
 }
 
 static u64 of_bus_default_map(__be32 *addr, const __be32 *range,
@@ -155,8 +155,8 @@ static int of_bus_pci_match(struct device_node *np)
 		of_node_is_pcie(np);
 }
 
-static void of_bus_pci_count_cells(struct device_node *np,
-				   int *addrc, int *sizec)
+static void of_bus_pci_count_cells(struct device_node *np, int *addrc, int *sizec,
+				   unsigned int flags)
 {
 	if (addrc)
 		*addrc = 3;
@@ -272,8 +272,8 @@ static int of_bus_isa_match(struct device_node *np)
 	return of_node_name_eq(np, "isa");
 }
 
-static void of_bus_isa_count_cells(struct device_node *child,
-				   int *addrc, int *sizec)
+static void of_bus_isa_count_cells(struct device_node *child, int *addrc, int *sizec,
+				   unsigned int flags)
 {
 	if (addrc)
 		*addrc = 2;
@@ -475,6 +475,7 @@ static u64 __of_translate_address(struct device_node *dev,
 				  const __be32 *in_addr, const char *rprop,
 				  struct device_node **host)
 {
+	unsigned int flags = (rprop && !strcmp(rprop, "dma-ranges")) ? OF_CELLS_DMA : 0;
 	struct device_node *parent = NULL;
 	struct of_bus *bus, *pbus;
 	__be32 addr[OF_MAX_ADDR_CELLS];
@@ -494,7 +495,7 @@ static u64 __of_translate_address(struct device_node *dev,
 	bus = of_match_bus(parent);
 
 	/* Count address cells & copy address locally */
-	bus->count_cells(dev, &na, &ns);
+	bus->count_cells(dev, &na, &ns, flags);
 	if (!OF_CHECK_COUNTS(na, ns)) {
 		pr_debug("Bad cell count for %pOF\n", dev);
 		goto bail;
@@ -536,7 +537,7 @@ static u64 __of_translate_address(struct device_node *dev,
 
 		/* Get new parent bus and counts */
 		pbus = of_match_bus(parent);
-		pbus->count_cells(dev, &pna, &pns);
+		pbus->count_cells(dev, &pna, &pns, flags);
 		if (!OF_CHECK_COUNTS(pna, pns)) {
 			pr_err("Bad cell count for %pOF\n", dev);
 			break;
@@ -644,7 +645,7 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
 		of_node_put(parent);
 		return NULL;
 	}
-	bus->count_cells(dev, &na, &ns);
+	bus->count_cells(dev, &na, &ns, false);
 	of_node_put(parent);
 	if (!OF_CHECK_ADDR_COUNT(na))
 		return NULL;
@@ -677,11 +678,15 @@ static int parser_init(struct of_pci_range_parser *parser,
 {
 	int rlen;
 
+	if (strcmp(name, "dma-ranges") == 0)
+		parser->flags = OF_CELLS_DMA;
+	else
+		parser->flags = 0;
+
 	parser->node = node;
-	parser->pna = of_n_addr_cells(node);
-	parser->na = of_bus_n_addr_cells(node);
-	parser->ns = of_bus_n_size_cells(node);
-	parser->dma = !strcmp(name, "dma-ranges");
+	parser->pna = of_addr_cells(node, parser->flags);
+	parser->na = of_bus_addr_cells(node, parser->flags);
+	parser->ns = of_bus_size_cells(node, parser->flags);
 	parser->bus = of_match_bus(node);
 
 	parser->range = of_get_property(node, name, &rlen);
@@ -730,7 +735,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
 
 	range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na);
 
-	if (parser->dma)
+	if (parser->flags & OF_CELLS_DMA)
 		range->cpu_addr = of_translate_dma_address(parser->node,
 				parser->range + na);
 	else
@@ -747,7 +752,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
 
 		flags = parser->bus->get_flags(parser->range);
 		bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na);
-		if (parser->dma)
+		if (parser->flags & OF_CELLS_DMA)
 			cpu_addr = of_translate_dma_address(parser->node,
 					parser->range + na);
 		else
diff --git a/drivers/of/base.c b/drivers/of/base.c
index d5a5c35eba72..39556fa7a13b 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -86,47 +86,89 @@ static bool __of_node_is_type(const struct device_node *np, const char *type)
 	return np && match && type && !strcmp(match, type);
 }
 
-int of_bus_n_addr_cells(struct device_node *np)
+int of_bus_addr_cells(struct device_node *np, unsigned int flags)
 {
+	struct device_node *parent;
 	u32 cells;
 
-	for (; np; np = np->parent)
+	while (np) {
+		if (flags & OF_CELLS_DMA)
+			if (!of_property_read_u32(np, "#dma-address-cells", &cells))
+				return cells;
+
 		if (!of_property_read_u32(np, "#address-cells", &cells))
 			return cells;
 
+		if (flags & OF_CELLS_DMA)
+			parent = __of_get_dma_parent(np);
+		else
+			parent = of_node_get(np->parent);
+
+		of_node_put(np);
+		np = parent;
+	}
+
 	/* No #address-cells property for the root node */
 	return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
 }
 
-int of_n_addr_cells(struct device_node *np)
+int of_addr_cells(struct device_node *np, unsigned int flags)
 {
-	if (np->parent)
-		np = np->parent;
+	int cells;
+
+	if (flags & OF_CELLS_DMA)
+		np = __of_get_dma_parent(np);
+	else if (np->parent)
+		np = of_node_get(np->parent);
+
+	cells = of_bus_addr_cells(np, flags);
 
-	return of_bus_n_addr_cells(np);
+	of_node_put(np);
+	return cells;
 }
-EXPORT_SYMBOL(of_n_addr_cells);
+EXPORT_SYMBOL(of_addr_cells);
 
-int of_bus_n_size_cells(struct device_node *np)
+int of_bus_size_cells(struct device_node *np, unsigned int flags)
 {
+	struct device_node *parent;
 	u32 cells;
 
-	for (; np; np = np->parent)
+	while (np) {
+		if (flags & OF_CELLS_DMA)
+			if (!of_property_read_u32(np, "#dma-size-cells", &cells))
+				return cells;
+
 		if (!of_property_read_u32(np, "#size-cells", &cells))
 			return cells;
 
+		if (flags & OF_CELLS_DMA)
+			parent = __of_get_dma_parent(np);
+		else
+			parent = of_node_get(np->parent);
+
+		of_node_put(np);
+		np = parent;
+	}
+
 	/* No #size-cells property for the root node */
 	return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
 }
 
-int of_n_size_cells(struct device_node *np)
+int of_size_cells(struct device_node *np, unsigned int flags)
 {
-	if (np->parent)
-		np = np->parent;
+	int cells;
+
+	if (flags & OF_CELLS_DMA)
+		np = __of_get_dma_parent(np);
+	else if (np->parent)
+		np = of_node_get(np->parent);
+
+	cells = of_bus_size_cells(np, flags);
 
-	return of_bus_n_size_cells(np);
+	of_node_put(np);
+	return cells;
 }
-EXPORT_SYMBOL(of_n_size_cells);
+EXPORT_SYMBOL(of_size_cells);
 
 #ifdef CONFIG_NUMA
 int __weak of_node_to_nid(struct device_node *np)
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index fb6792d381a6..f57900c3f25c 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -148,8 +148,18 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np,
 #define for_each_transaction_entry_reverse(_oft, _te) \
 	list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
 
-extern int of_bus_n_addr_cells(struct device_node *np);
-extern int of_bus_n_size_cells(struct device_node *np);
+extern int of_bus_addr_cells(struct device_node *np, unsigned int flags);
+extern int of_bus_size_cells(struct device_node *np, unsigned int flags);
+
+static inline int of_bus_n_addr_cells(struct device_node *np)
+{
+	return of_bus_addr_cells(np, 0);
+}
+
+static inline int of_bus_n_size_cells(struct device_node *np)
+{
+	return of_bus_size_cells(np, 0);
+}
 
 struct bus_dma_region;
 #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA)
diff --git a/include/linux/of.h b/include/linux/of.h
index 8b9f94386dc3..dd4d4b3c8636 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -358,8 +358,21 @@ extern u64 of_get_cpu_hwid(struct device_node *cpun, unsigned int thread);
 #define for_each_property_of_node(dn, pp) \
 	for (pp = dn->properties; pp != NULL; pp = pp->next)
 
-extern int of_n_addr_cells(struct device_node *np);
-extern int of_n_size_cells(struct device_node *np);
+#define OF_CELLS_DMA 0x1
+
+extern int of_addr_cells(struct device_node *np, unsigned int flags);
+extern int of_size_cells(struct device_node *np, unsigned int flags);
+
+static inline int of_n_addr_cells(struct device_node *np)
+{
+	return of_addr_cells(np, 0);
+}
+
+static inline int of_n_size_cells(struct device_node *np)
+{
+	return of_size_cells(np, 0);
+}
+
 extern const struct of_device_id *of_match_node(
 	const struct of_device_id *matches, const struct device_node *node);
 extern int of_modalias_node(struct device_node *node, char *modalias, int len);
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 265f26eeaf6b..15354085ea35 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -16,7 +16,7 @@ struct of_pci_range_parser {
 	int na;
 	int ns;
 	int pna;
-	bool dma;
+	unsigned int flags;
 };
 #define of_range_parser of_pci_range_parser
 
-- 
2.38.1


  reply	other threads:[~2022-11-11 16:18 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-11 16:18 [PATCH v11 0/5] iommu: Support mappings/reservations in reserved-memory regions Thierry Reding
2022-11-11 16:18 ` Thierry Reding [this message]
2022-11-11 16:18 ` [PATCH v11 2/5] of: Introduce of_translate_dma_region() Thierry Reding
2022-11-11 16:18 ` [PATCH v11 3/5] dt-bindings: reserved-memory: Document iommu-addresses Thierry Reding
2022-11-11 16:18 ` [PATCH v11 4/5] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
2022-11-11 16:18 ` [PATCH v11 5/5] iommu: dma: Use of_iommu_get_resv_regions() Thierry Reding
2022-11-19  9:39 ` [PATCH v11 0/5] iommu: Support mappings/reservations in reserved-memory regions Joerg Roedel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221111161806.630527-2-thierry.reding@gmail.com \
    --to=thierry.reding@gmail.com \
    --cc=alyssa.rosenzweig@collabora.com \
    --cc=amhetre@nvidia.com \
    --cc=asahi@lists.linux.dev \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.osipenko@collabora.com \
    --cc=iommu@lists.linux-foundation.org \
    --cc=j@jannau.net \
    --cc=joro@8bytes.org \
    --cc=l.stach@pengutronix.de \
    --cc=linux-tegra@vger.kernel.org \
    --cc=nicolinc@nvidia.com \
    --cc=robh+dt@kernel.org \
    --cc=robin.murphy@arm.com \
    --cc=spujar@nvidia.com \
    --cc=vdumpa@nvidia.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).