aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Murray <Andrew.Murray@arm.com>2013-05-16 11:55:17 -0400
committerJason Cooper <jason@lakedaemon.net>2013-05-19 16:29:55 -0400
commit29b635c00f3ebcdaf7a52c4948f6d948ad3757d3 (patch)
treea6e09cffad345acacef84d646d7b91c005e91a4d
parentf722406faae2d073cc1d01063d1123c35425939e (diff)
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>
-rw-r--r--drivers/of/address.c67
-rw-r--r--include/linux/of_address.h48
2 files changed, 115 insertions, 0 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 04da786c84d2..fdd0636a987d 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -227,6 +227,73 @@ int of_pci_address_to_resource(struct device_node *dev, int bar,
227 return __of_address_to_resource(dev, addrp, size, flags, NULL, r); 227 return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
228} 228}
229EXPORT_SYMBOL_GPL(of_pci_address_to_resource); 229EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
230
231int of_pci_range_parser_init(struct of_pci_range_parser *parser,
232 struct device_node *node)
233{
234 const int na = 3, ns = 2;
235 int rlen;
236
237 parser->node = node;
238 parser->pna = of_n_addr_cells(node);
239 parser->np = parser->pna + na + ns;
240
241 parser->range = of_get_property(node, "ranges", &rlen);
242 if (parser->range == NULL)
243 return -ENOENT;
244
245 parser->end = parser->range + rlen / sizeof(__be32);
246
247 return 0;
248}
249EXPORT_SYMBOL_GPL(of_pci_range_parser_init);
250
251struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
252 struct of_pci_range *range)
253{
254 const int na = 3, ns = 2;
255
256 if (!range)
257 return NULL;
258
259 if (!parser->range || parser->range + parser->np > parser->end)
260 return NULL;
261
262 range->pci_space = parser->range[0];
263 range->flags = of_bus_pci_get_flags(parser->range);
264 range->pci_addr = of_read_number(parser->range + 1, ns);
265 range->cpu_addr = of_translate_address(parser->node,
266 parser->range + na);
267 range->size = of_read_number(parser->range + parser->pna + na, ns);
268
269 parser->range += parser->np;
270
271 /* Now consume following elements while they are contiguous */
272 while (parser->range + parser->np <= parser->end) {
273 u32 flags, pci_space;
274 u64 pci_addr, cpu_addr, size;
275
276 pci_space = be32_to_cpup(parser->range);
277 flags = of_bus_pci_get_flags(parser->range);
278 pci_addr = of_read_number(parser->range + 1, ns);
279 cpu_addr = of_translate_address(parser->node,
280 parser->range + na);
281 size = of_read_number(parser->range + parser->pna + na, ns);
282
283 if (flags != range->flags)
284 break;
285 if (pci_addr != range->pci_addr + range->size ||
286 cpu_addr != range->cpu_addr + range->size)
287 break;
288
289 range->size += size;
290 parser->range += parser->np;
291 }
292
293 return range;
294}
295EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
296
230#endif /* CONFIG_PCI */ 297#endif /* CONFIG_PCI */
231 298
232/* 299/*
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 0506eb53519b..4c2e6f26432c 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -4,6 +4,36 @@
4#include <linux/errno.h> 4#include <linux/errno.h>
5#include <linux/of.h> 5#include <linux/of.h>
6 6
7struct of_pci_range_parser {
8 struct device_node *node;
9 const __be32 *range;
10 const __be32 *end;
11 int np;
12 int pna;
13};
14
15struct of_pci_range {
16 u32 pci_space;
17 u64 pci_addr;
18 u64 cpu_addr;
19 u64 size;
20 u32 flags;
21};
22
23#define for_each_of_pci_range(parser, range) \
24 for (; of_pci_range_parser_one(parser, range);)
25
26static inline void of_pci_range_to_resource(struct of_pci_range *range,
27 struct device_node *np,
28 struct resource *res)
29{
30 res->flags = range->flags;
31 res->start = range->cpu_addr;
32 res->end = range->cpu_addr + range->size - 1;
33 res->parent = res->child = res->sibling = NULL;
34 res->name = np->full_name;
35}
36
7#ifdef CONFIG_OF_ADDRESS 37#ifdef CONFIG_OF_ADDRESS
8extern u64 of_translate_address(struct device_node *np, const __be32 *addr); 38extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
9extern bool of_can_translate_address(struct device_node *dev); 39extern bool of_can_translate_address(struct device_node *dev);
@@ -27,6 +57,11 @@ static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
27#define pci_address_to_pio pci_address_to_pio 57#define pci_address_to_pio pci_address_to_pio
28#endif 58#endif
29 59
60extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
61 struct device_node *node);
62extern struct of_pci_range *of_pci_range_parser_one(
63 struct of_pci_range_parser *parser,
64 struct of_pci_range *range);
30#else /* CONFIG_OF_ADDRESS */ 65#else /* CONFIG_OF_ADDRESS */
31#ifndef of_address_to_resource 66#ifndef of_address_to_resource
32static inline int of_address_to_resource(struct device_node *dev, int index, 67static inline int of_address_to_resource(struct device_node *dev, int index,
@@ -53,6 +88,19 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
53{ 88{
54 return NULL; 89 return NULL;
55} 90}
91
92static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
93 struct device_node *node)
94{
95 return -1;
96}
97
98static inline struct of_pci_range *of_pci_range_parser_one(
99 struct of_pci_range_parser *parser,
100 struct of_pci_range *range)
101{
102 return NULL;
103}
56#endif /* CONFIG_OF_ADDRESS */ 104#endif /* CONFIG_OF_ADDRESS */
57 105
58 106