]>
Commit | Line | Data |
---|---|---|
f7075c3a SS |
1 | From: Andrew Murray <Andrew.Murray@arm.com> |
2 | Subject: [PATCH] of/pci: Provide support for parsing PCI DT ranges property | |
3 | ||
4 | This patch factors out common implementation patterns to reduce overall kernel | |
5 | code and provide a means for host bridge drivers to directly obtain struct | |
6 | resources from the DT's ranges property without relying on architecture specific | |
7 | DT handling. This will make it easier to write archiecture independent host bridge | |
8 | drivers and mitigate against further duplication of DT parsing code. | |
9 | ||
10 | This patch can be used in the following way: | |
11 | ||
12 | struct of_pci_range_parser parser; | |
13 | struct of_pci_range range; | |
14 | ||
15 | if (of_pci_range_parser_init(&parser, np)) | |
16 | ; //no ranges property | |
17 | ||
18 | for_each_of_pci_range(&parser, &range) { | |
19 | ||
20 | /* | |
21 | directly access properties of the address range, e.g.: | |
22 | range.pci_space, range.pci_addr, range.cpu_addr, | |
23 | range.size, range.flags | |
24 | ||
25 | alternatively obtain a struct resource, e.g.: | |
26 | struct resource res; | |
27 | of_pci_range_to_resource(&range, np, &res); | |
28 | */ | |
29 | } | |
30 | ||
31 | Additionally the implementation takes care of adjacent ranges and merges them | |
32 | into a single range (as was the case with powerpc and microblaze). | |
33 | ||
34 | Signed-off-by: Andrew Murray <Andrew.Murray@arm.com> | |
35 | Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com> | |
36 | Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
37 | Reviewed-by: Rob Herring <rob.herring@calxeda.com> | |
38 | Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
39 | Tested-by: Linus Walleij <linus.walleij@linaro.org> | |
40 | Tested-by: Jingoo Han <jg1.han@samsung.com> | |
41 | Acked-by: Grant Likely <grant.likely@secretlab.ca> | |
42 | Signed-off-by: Jason Cooper <jason@lakedaemon.net> | |
43 | --- | |
44 | drivers/of/address.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ | |
45 | include/linux/of_address.h | 48 +++++++++++++++++++++++++++++++++ | |
46 | 2 files changed, 115 insertions(+) | |
47 | ||
48 | --- a/drivers/of/address.c | |
49 | +++ b/drivers/of/address.c | |
50 | @@ -231,6 +231,73 @@ int of_pci_address_to_resource(struct de | |
51 | return __of_address_to_resource(dev, addrp, size, flags, NULL, r); | |
52 | } | |
53 | EXPORT_SYMBOL_GPL(of_pci_address_to_resource); | |
54 | + | |
55 | +int of_pci_range_parser_init(struct of_pci_range_parser *parser, | |
56 | + struct device_node *node) | |
57 | +{ | |
58 | + const int na = 3, ns = 2; | |
59 | + int rlen; | |
60 | + | |
61 | + parser->node = node; | |
62 | + parser->pna = of_n_addr_cells(node); | |
63 | + parser->np = parser->pna + na + ns; | |
64 | + | |
65 | + parser->range = of_get_property(node, "ranges", &rlen); | |
66 | + if (parser->range == NULL) | |
67 | + return -ENOENT; | |
68 | + | |
69 | + parser->end = parser->range + rlen / sizeof(__be32); | |
70 | + | |
71 | + return 0; | |
72 | +} | |
73 | +EXPORT_SYMBOL_GPL(of_pci_range_parser_init); | |
74 | + | |
75 | +struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, | |
76 | + struct of_pci_range *range) | |
77 | +{ | |
78 | + const int na = 3, ns = 2; | |
79 | + | |
80 | + if (!range) | |
81 | + return NULL; | |
82 | + | |
83 | + if (!parser->range || parser->range + parser->np > parser->end) | |
84 | + return NULL; | |
85 | + | |
86 | + range->pci_space = parser->range[0]; | |
87 | + range->flags = of_bus_pci_get_flags(parser->range); | |
88 | + range->pci_addr = of_read_number(parser->range + 1, ns); | |
89 | + range->cpu_addr = of_translate_address(parser->node, | |
90 | + parser->range + na); | |
91 | + range->size = of_read_number(parser->range + parser->pna + na, ns); | |
92 | + | |
93 | + parser->range += parser->np; | |
94 | + | |
95 | + /* Now consume following elements while they are contiguous */ | |
96 | + while (parser->range + parser->np <= parser->end) { | |
97 | + u32 flags, pci_space; | |
98 | + u64 pci_addr, cpu_addr, size; | |
99 | + | |
100 | + pci_space = be32_to_cpup(parser->range); | |
101 | + flags = of_bus_pci_get_flags(parser->range); | |
102 | + pci_addr = of_read_number(parser->range + 1, ns); | |
103 | + cpu_addr = of_translate_address(parser->node, | |
104 | + parser->range + na); | |
105 | + size = of_read_number(parser->range + parser->pna + na, ns); | |
106 | + | |
107 | + if (flags != range->flags) | |
108 | + break; | |
109 | + if (pci_addr != range->pci_addr + range->size || | |
110 | + cpu_addr != range->cpu_addr + range->size) | |
111 | + break; | |
112 | + | |
113 | + range->size += size; | |
114 | + parser->range += parser->np; | |
115 | + } | |
116 | + | |
117 | + return range; | |
118 | +} | |
119 | +EXPORT_SYMBOL_GPL(of_pci_range_parser_one); | |
120 | + | |
121 | #endif /* CONFIG_PCI */ | |
122 | ||
123 | /* | |
124 | --- a/include/linux/of_address.h | |
125 | +++ b/include/linux/of_address.h | |
126 | @@ -4,6 +4,36 @@ | |
127 | #include <linux/errno.h> | |
128 | #include <linux/of.h> | |
129 | ||
130 | +struct of_pci_range_parser { | |
131 | + struct device_node *node; | |
132 | + const __be32 *range; | |
133 | + const __be32 *end; | |
134 | + int np; | |
135 | + int pna; | |
136 | +}; | |
137 | + | |
138 | +struct of_pci_range { | |
139 | + u32 pci_space; | |
140 | + u64 pci_addr; | |
141 | + u64 cpu_addr; | |
142 | + u64 size; | |
143 | + u32 flags; | |
144 | +}; | |
145 | + | |
146 | +#define for_each_of_pci_range(parser, range) \ | |
147 | + for (; of_pci_range_parser_one(parser, range);) | |
148 | + | |
149 | +static inline void of_pci_range_to_resource(struct of_pci_range *range, | |
150 | + struct device_node *np, | |
151 | + struct resource *res) | |
152 | +{ | |
153 | + res->flags = range->flags; | |
154 | + res->start = range->cpu_addr; | |
155 | + res->end = range->cpu_addr + range->size - 1; | |
156 | + res->parent = res->child = res->sibling = NULL; | |
157 | + res->name = np->full_name; | |
158 | +} | |
159 | + | |
160 | #ifdef CONFIG_OF_ADDRESS | |
161 | extern u64 of_translate_address(struct device_node *np, const __be32 *addr); | |
162 | extern bool of_can_translate_address(struct device_node *dev); | |
163 | @@ -27,6 +57,11 @@ static inline unsigned long pci_address_ | |
164 | #define pci_address_to_pio pci_address_to_pio | |
165 | #endif | |
166 | ||
167 | +extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, | |
168 | + struct device_node *node); | |
169 | +extern struct of_pci_range *of_pci_range_parser_one( | |
170 | + struct of_pci_range_parser *parser, | |
171 | + struct of_pci_range *range); | |
172 | #else /* CONFIG_OF_ADDRESS */ | |
173 | #ifndef of_address_to_resource | |
174 | static inline int of_address_to_resource(struct device_node *dev, int index, | |
175 | @@ -53,6 +88,19 @@ static inline const __be32 *of_get_addre | |
176 | { | |
177 | return NULL; | |
178 | } | |
179 | + | |
180 | +static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser, | |
181 | + struct device_node *node) | |
182 | +{ | |
183 | + return -1; | |
184 | +} | |
185 | + | |
186 | +static inline struct of_pci_range *of_pci_range_parser_one( | |
187 | + struct of_pci_range_parser *parser, | |
188 | + struct of_pci_range *range) | |
189 | +{ | |
190 | + return NULL; | |
191 | +} | |
192 | #endif /* CONFIG_OF_ADDRESS */ | |
193 | ||
194 |