aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiviu Dudau <Liviu.Dudau@arm.com>2014-09-29 10:29:28 -0400
committerBjorn Helgaas <bhelgaas@google.com>2014-09-30 19:08:57 -0400
commitcbe4097f8ae699ebbdaf8c95ecab38d47e0bd5da (patch)
tree3abf75a2058642b3296f997e95a2f32a96e99c20
parent41e5c0f81d3e676d671d96a0a1fafb27abfbd9d7 (diff)
of/pci: Add support for parsing PCI host bridge resources from DT
Provide a function to parse the PCI DT ranges that can be used to create a pci_host_bridge structure together with its associated bus. Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com> [make io_base parameter optional] Signed-off-by: Robert Richter <rrichter@cavium.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> CC: Arnd Bergmann <arnd@arndb.de> CC: Grant Likely <grant.likely@linaro.org> CC: Rob Herring <robh+dt@kernel.org> CC: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--drivers/of/of_pci.c117
-rw-r--r--include/linux/of_pci.h6
2 files changed, 123 insertions, 0 deletions
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 82d172fa145a..8882b467be95 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -1,7 +1,9 @@
1#include <linux/kernel.h> 1#include <linux/kernel.h>
2#include <linux/export.h> 2#include <linux/export.h>
3#include <linux/of.h> 3#include <linux/of.h>
4#include <linux/of_address.h>
4#include <linux/of_pci.h> 5#include <linux/of_pci.h>
6#include <linux/slab.h>
5 7
6static inline int __of_pci_pci_compare(struct device_node *node, 8static inline int __of_pci_pci_compare(struct device_node *node,
7 unsigned int data) 9 unsigned int data)
@@ -114,6 +116,121 @@ int of_get_pci_domain_nr(struct device_node *node)
114} 116}
115EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); 117EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
116 118
119#if defined(CONFIG_OF_ADDRESS)
120/**
121 * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT
122 * @dev: device node of the host bridge having the range property
123 * @busno: bus number associated with the bridge root bus
124 * @bus_max: maximum number of buses for this bridge
125 * @resources: list where the range of resources will be added after DT parsing
126 * @io_base: pointer to a variable that will contain on return the physical
127 * address for the start of the I/O range. Can be NULL if the caller doesn't
128 * expect IO ranges to be present in the device tree.
129 *
130 * It is the caller's job to free the @resources list.
131 *
132 * This function will parse the "ranges" property of a PCI host bridge device
133 * node and setup the resource mapping based on its content. It is expected
134 * that the property conforms with the Power ePAPR document.
135 *
136 * It returns zero if the range parsing has been successful or a standard error
137 * value if it failed.
138 */
139int of_pci_get_host_bridge_resources(struct device_node *dev,
140 unsigned char busno, unsigned char bus_max,
141 struct list_head *resources, resource_size_t *io_base)
142{
143 struct resource *res;
144 struct resource *bus_range;
145 struct of_pci_range range;
146 struct of_pci_range_parser parser;
147 char range_type[4];
148 int err;
149
150 if (io_base)
151 *io_base = (resource_size_t)OF_BAD_ADDR;
152
153 bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
154 if (!bus_range)
155 return -ENOMEM;
156
157 pr_info("PCI host bridge %s ranges:\n", dev->full_name);
158
159 err = of_pci_parse_bus_range(dev, bus_range);
160 if (err) {
161 bus_range->start = busno;
162 bus_range->end = bus_max;
163 bus_range->flags = IORESOURCE_BUS;
164 pr_info(" No bus range found for %s, using %pR\n",
165 dev->full_name, bus_range);
166 } else {
167 if (bus_range->end > bus_range->start + bus_max)
168 bus_range->end = bus_range->start + bus_max;
169 }
170 pci_add_resource(resources, bus_range);
171
172 /* Check for ranges property */
173 err = of_pci_range_parser_init(&parser, dev);
174 if (err)
175 goto parse_failed;
176
177 pr_debug("Parsing ranges property...\n");
178 for_each_of_pci_range(&parser, &range) {
179 /* Read next ranges element */
180 if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
181 snprintf(range_type, 4, " IO");
182 else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
183 snprintf(range_type, 4, "MEM");
184 else
185 snprintf(range_type, 4, "err");
186 pr_info(" %s %#010llx..%#010llx -> %#010llx\n", range_type,
187 range.cpu_addr, range.cpu_addr + range.size - 1,
188 range.pci_addr);
189
190 /*
191 * If we failed translation or got a zero-sized region
192 * then skip this range
193 */
194 if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
195 continue;
196
197 res = kzalloc(sizeof(struct resource), GFP_KERNEL);
198 if (!res) {
199 err = -ENOMEM;
200 goto parse_failed;
201 }
202
203 err = of_pci_range_to_resource(&range, dev, res);
204 if (err)
205 goto conversion_failed;
206
207 if (resource_type(res) == IORESOURCE_IO) {
208 if (!io_base) {
209 pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
210 dev->full_name);
211 err = -EINVAL;
212 goto conversion_failed;
213 }
214 if (*io_base != (resource_size_t)OF_BAD_ADDR)
215 pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
216 dev->full_name);
217 *io_base = range.cpu_addr;
218 }
219
220 pci_add_resource_offset(resources, res, res->start - range.pci_addr);
221 }
222
223 return 0;
224
225conversion_failed:
226 kfree(res);
227parse_failed:
228 pci_free_resource_list(resources);
229 return err;
230}
231EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
232#endif /* CONFIG_OF_ADDRESS */
233
117#ifdef CONFIG_PCI_MSI 234#ifdef CONFIG_PCI_MSI
118 235
119static LIST_HEAD(of_pci_msi_chip_list); 236static LIST_HEAD(of_pci_msi_chip_list);
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index 71062e9602f5..1fd207e7a847 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -52,6 +52,12 @@ of_get_pci_domain_nr(struct device_node *node)
52} 52}
53#endif 53#endif
54 54
55#if defined(CONFIG_OF_ADDRESS)
56int of_pci_get_host_bridge_resources(struct device_node *dev,
57 unsigned char busno, unsigned char bus_max,
58 struct list_head *resources, resource_size_t *io_base);
59#endif
60
55#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) 61#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
56int of_pci_msi_chip_add(struct msi_chip *chip); 62int of_pci_msi_chip_add(struct msi_chip *chip);
57void of_pci_msi_chip_remove(struct msi_chip *chip); 63void of_pci_msi_chip_remove(struct msi_chip *chip);