195 lines
5.7 KiB
Diff
195 lines
5.7 KiB
Diff
From: Andrew Murray <Andrew.Murray@arm.com>
|
|
Subject: [PATCH] of/pci: Provide support for parsing PCI DT ranges property
|
|
|
|
This patch factors out common implementation patterns to reduce overall kernel
|
|
code and provide a means for host bridge drivers to directly obtain struct
|
|
resources from the DT's ranges property without relying on architecture specific
|
|
DT handling. This will make it easier to write archiecture independent host bridge
|
|
drivers and mitigate against further duplication of DT parsing code.
|
|
|
|
This patch can be used in the following way:
|
|
|
|
struct of_pci_range_parser parser;
|
|
struct of_pci_range range;
|
|
|
|
if (of_pci_range_parser_init(&parser, np))
|
|
; //no ranges property
|
|
|
|
for_each_of_pci_range(&parser, &range) {
|
|
|
|
/*
|
|
directly access properties of the address range, e.g.:
|
|
range.pci_space, range.pci_addr, range.cpu_addr,
|
|
range.size, range.flags
|
|
|
|
alternatively obtain a struct resource, e.g.:
|
|
struct resource res;
|
|
of_pci_range_to_resource(&range, np, &res);
|
|
*/
|
|
}
|
|
|
|
Additionally the implementation takes care of adjacent ranges and merges them
|
|
into a single range (as was the case with powerpc and microblaze).
|
|
|
|
Signed-off-by: Andrew Murray <Andrew.Murray@arm.com>
|
|
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
|
|
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
|
Reviewed-by: Rob Herring <rob.herring@calxeda.com>
|
|
Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
|
Tested-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Tested-by: Jingoo Han <jg1.han@samsung.com>
|
|
Acked-by: Grant Likely <grant.likely@secretlab.ca>
|
|
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
|
|
---
|
|
drivers/of/address.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
include/linux/of_address.h | 48 +++++++++++++++++++++++++++++++++
|
|
2 files changed, 115 insertions(+)
|
|
|
|
--- a/drivers/of/address.c
|
|
+++ b/drivers/of/address.c
|
|
@@ -231,6 +231,73 @@ int of_pci_address_to_resource(struct de
|
|
return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
|
|
+
|
|
+int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
|
+ struct device_node *node)
|
|
+{
|
|
+ const int na = 3, ns = 2;
|
|
+ int rlen;
|
|
+
|
|
+ parser->node = node;
|
|
+ parser->pna = of_n_addr_cells(node);
|
|
+ parser->np = parser->pna + na + ns;
|
|
+
|
|
+ parser->range = of_get_property(node, "ranges", &rlen);
|
|
+ if (parser->range == NULL)
|
|
+ return -ENOENT;
|
|
+
|
|
+ parser->end = parser->range + rlen / sizeof(__be32);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(of_pci_range_parser_init);
|
|
+
|
|
+struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
|
|
+ struct of_pci_range *range)
|
|
+{
|
|
+ const int na = 3, ns = 2;
|
|
+
|
|
+ if (!range)
|
|
+ return NULL;
|
|
+
|
|
+ if (!parser->range || parser->range + parser->np > parser->end)
|
|
+ return NULL;
|
|
+
|
|
+ range->pci_space = parser->range[0];
|
|
+ range->flags = of_bus_pci_get_flags(parser->range);
|
|
+ range->pci_addr = of_read_number(parser->range + 1, ns);
|
|
+ range->cpu_addr = of_translate_address(parser->node,
|
|
+ parser->range + na);
|
|
+ range->size = of_read_number(parser->range + parser->pna + na, ns);
|
|
+
|
|
+ parser->range += parser->np;
|
|
+
|
|
+ /* Now consume following elements while they are contiguous */
|
|
+ while (parser->range + parser->np <= parser->end) {
|
|
+ u32 flags, pci_space;
|
|
+ u64 pci_addr, cpu_addr, size;
|
|
+
|
|
+ pci_space = be32_to_cpup(parser->range);
|
|
+ flags = of_bus_pci_get_flags(parser->range);
|
|
+ pci_addr = of_read_number(parser->range + 1, ns);
|
|
+ cpu_addr = of_translate_address(parser->node,
|
|
+ parser->range + na);
|
|
+ size = of_read_number(parser->range + parser->pna + na, ns);
|
|
+
|
|
+ if (flags != range->flags)
|
|
+ break;
|
|
+ if (pci_addr != range->pci_addr + range->size ||
|
|
+ cpu_addr != range->cpu_addr + range->size)
|
|
+ break;
|
|
+
|
|
+ range->size += size;
|
|
+ parser->range += parser->np;
|
|
+ }
|
|
+
|
|
+ return range;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
|
|
+
|
|
#endif /* CONFIG_PCI */
|
|
|
|
/*
|
|
--- a/include/linux/of_address.h
|
|
+++ b/include/linux/of_address.h
|
|
@@ -4,6 +4,36 @@
|
|
#include <linux/errno.h>
|
|
#include <linux/of.h>
|
|
|
|
+struct of_pci_range_parser {
|
|
+ struct device_node *node;
|
|
+ const __be32 *range;
|
|
+ const __be32 *end;
|
|
+ int np;
|
|
+ int pna;
|
|
+};
|
|
+
|
|
+struct of_pci_range {
|
|
+ u32 pci_space;
|
|
+ u64 pci_addr;
|
|
+ u64 cpu_addr;
|
|
+ u64 size;
|
|
+ u32 flags;
|
|
+};
|
|
+
|
|
+#define for_each_of_pci_range(parser, range) \
|
|
+ for (; of_pci_range_parser_one(parser, range);)
|
|
+
|
|
+static inline void of_pci_range_to_resource(struct of_pci_range *range,
|
|
+ struct device_node *np,
|
|
+ struct resource *res)
|
|
+{
|
|
+ res->flags = range->flags;
|
|
+ res->start = range->cpu_addr;
|
|
+ res->end = range->cpu_addr + range->size - 1;
|
|
+ res->parent = res->child = res->sibling = NULL;
|
|
+ res->name = np->full_name;
|
|
+}
|
|
+
|
|
#ifdef CONFIG_OF_ADDRESS
|
|
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
|
|
extern bool of_can_translate_address(struct device_node *dev);
|
|
@@ -27,6 +57,11 @@ static inline unsigned long pci_address_
|
|
#define pci_address_to_pio pci_address_to_pio
|
|
#endif
|
|
|
|
+extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
|
+ struct device_node *node);
|
|
+extern struct of_pci_range *of_pci_range_parser_one(
|
|
+ struct of_pci_range_parser *parser,
|
|
+ struct of_pci_range *range);
|
|
#else /* CONFIG_OF_ADDRESS */
|
|
#ifndef of_address_to_resource
|
|
static inline int of_address_to_resource(struct device_node *dev, int index,
|
|
@@ -53,6 +88,19 @@ static inline const __be32 *of_get_addre
|
|
{
|
|
return NULL;
|
|
}
|
|
+
|
|
+static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
|
+ struct device_node *node)
|
|
+{
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static inline struct of_pci_range *of_pci_range_parser_one(
|
|
+ struct of_pci_range_parser *parser,
|
|
+ struct of_pci_range *range)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
#endif /* CONFIG_OF_ADDRESS */
|
|
|
|
|