diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 15:03:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 15:03:49 -0400 |
commit | 80213c03c4151d900cf293ef0fc51f8d88495e14 (patch) | |
tree | af2422fa255aed96c23cef894e0adbf817f30c45 /drivers/of | |
parent | ea584595fc85e65796335033dfca25ed655cd0ed (diff) | |
parent | f92d9ee3ab39841d1f29f2d1aa96ff7c74b36ee1 (diff) |
Merge tag 'pci-v3.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI updates from Bjorn Helgaas:
"The interesting things here are:
- Turn on Config Request Retry Status Software Visibility. This
caused hangs last time, but we included a fix this time.
- Rework PCI device configuration to use _HPP/_HPX more aggressively
- Allow PCI devices to be put into D3cold during system suspend
- Add arm64 PCI support
- Add APM X-Gene host bridge driver
- Add TI Keystone host bridge driver
- Add Xilinx AXI host bridge driver
More detailed summary:
Enumeration
- Check Vendor ID only for Config Request Retry Status (Rajat Jain)
- Enable Config Request Retry Status when supported (Rajat Jain)
- Add generic domain handling (Catalin Marinas)
- Generate uppercase hex for modalias interface class (Ricardo Ribalda Delgado)
Resource management
- Add missing MEM_64 mask in pci_assign_unassigned_bridge_resources() (Yinghai Lu)
- Increase IBM ipr SAS Crocodile BARs to at least system page size (Douglas Lehr)
PCI device hotplug
- Prevent NULL dereference during pciehp probe (Andreas Noever)
- Move _HPP & _HPX handling into core (Bjorn Helgaas)
- Apply _HPP to PCIe devices as well as PCI (Bjorn Helgaas)
- Apply _HPP/_HPX to display devices (Bjorn Helgaas)
- Preserve SERR & PARITY settings when applying _HPP/_HPX (Bjorn Helgaas)
- Preserve MPS and MRRS settings when applying _HPP/_HPX (Bjorn Helgaas)
- Apply _HPP/_HPX to all devices, not just hot-added ones (Bjorn Helgaas)
- Fix wait time in pciehp timeout message (Yinghai Lu)
- Add more pciehp Slot Control debug output (Yinghai Lu)
- Stop disabling pciehp notifications during init (Yinghai Lu)
MSI
- Remove arch_msi_check_device() (Alexander Gordeev)
- Rename pci_msi_check_device() to pci_msi_supported() (Alexander Gordeev)
- Move D0 check into pci_msi_check_device() (Alexander Gordeev)
- Remove unused kobject from struct msi_desc (Yijing Wang)
- Remove "pos" from the struct msi_desc msi_attrib (Yijing Wang)
- Add "msi_bus" sysfs MSI/MSI-X control for endpoints (Yijing Wang)
- Use __get_cached_msi_msg() instead of get_cached_msi_msg() (Yijing Wang)
- Use __read_msi_msg() instead of read_msi_msg() (Yijing Wang)
- Use __write_msi_msg() instead of write_msi_msg() (Yijing Wang)
Power management
- Drop unused runtime PM support code for PCIe ports (Rafael J. Wysocki)
- Allow PCI devices to be put into D3cold during system suspend (Rafael J. Wysocki)
AER
- Add additional AER error strings (Gong Chen)
- Make <linux/aer.h> standalone includable (Thierry Reding)
Virtualization
- Add ACS quirk for Solarflare SFC9120 & SFC9140 (Alex Williamson)
- Add ACS quirk for Intel 10G NICs (Alex Williamson)
- Add ACS quirk for AMD A88X southbridge (Marti Raudsepp)
- Remove unused pci_find_upstream_pcie_bridge(), pci_get_dma_source() (Alex Williamson)
- Add device flag helpers (Ethan Zhao)
- Assume all Mellanox devices have broken INTx masking (Gavin Shan)
Generic host bridge driver
- Fix ioport_map() for !CONFIG_GENERIC_IOMAP (Liviu Dudau)
- Add pci_register_io_range() and pci_pio_to_address() (Liviu Dudau)
- Define PCI_IOBASE as the base of virtual PCI IO space (Liviu Dudau)
- Fix the conversion of IO ranges into IO resources (Liviu Dudau)
- Add pci_get_new_domain_nr() and of_get_pci_domain_nr() (Liviu Dudau)
- Add support for parsing PCI host bridge resources from DT (Liviu Dudau)
- Add pci_remap_iospace() to map bus I/O resources (Liviu Dudau)
- Add arm64 architectural support for PCI (Liviu Dudau)
APM X-Gene
- Add APM X-Gene PCIe driver (Tanmay Inamdar)
- Add arm64 DT APM X-Gene PCIe device tree nodes (Tanmay Inamdar)
Freescale i.MX6
- Probe in module_init(), not fs_initcall() (Lucas Stach)
- Delay enabling reference clock for SS until it stabilizes (Tim Harvey)
Marvell MVEBU
- Fix uninitialized variable in mvebu_get_tgt_attr() (Thomas Petazzoni)
NVIDIA Tegra
- Make sure the PCIe PLL is really reset (Eric Yuen)
- Add error path tegra_msi_teardown_irq() cleanup (Jisheng Zhang)
- Fix extended configuration space mapping (Peter Daifuku)
- Implement resource hierarchy (Thierry Reding)
- Clear CLKREQ# enable on port disable (Thierry Reding)
- Add Tegra124 support (Thierry Reding)
ST Microelectronics SPEAr13xx
- Pass config resource through reg property (Pratyush Anand)
Synopsys DesignWare
- Use NULL instead of false (Fabio Estevam)
- Parse bus-range property from devicetree (Lucas Stach)
- Use pci_create_root_bus() instead of pci_scan_root_bus() (Lucas Stach)
- Remove pci_assign_unassigned_resources() (Lucas Stach)
- Check private_data validity in single place (Lucas Stach)
- Setup and clear exactly one MSI at a time (Lucas Stach)
- Remove open-coded bitmap operations (Lucas Stach)
- Fix configuration base address when using 'reg' (Minghuan Lian)
- Fix IO resource end address calculation (Minghuan Lian)
- Rename get_msi_data() to get_msi_addr() (Minghuan Lian)
- Add get_msi_data() to pcie_host_ops (Minghuan Lian)
- Add support for v3.65 hardware (Murali Karicheri)
- Fold struct pcie_port_info into struct pcie_port (Pratyush Anand)
TI Keystone
- Add TI Keystone PCIe driver (Murali Karicheri)
- Limit MRSS for all downstream devices (Murali Karicheri)
- Assume controller is already in RC mode (Murali Karicheri)
- Set device ID based on SoC to support multiple ports (Murali Karicheri)
Xilinx AXI
- Add Xilinx AXI PCIe driver (Srikanth Thokala)
- Fix xilinx_pcie_assign_msi() return value test (Dan Carpenter)
Miscellaneous
- Clean up whitespace (Quentin Lambert)
- Remove assignments from "if" conditions (Quentin Lambert)
- Move PCI_VENDOR_ID_VMWARE to pci_ids.h (Francesco Ruggeri)
- x86: Mark DMI tables as initialization data (Mathias Krause)
- x86: Move __init annotation to the correct place (Mathias Krause)
- x86: Mark constants of pci_mmcfg_nvidia_mcp55() as __initconst (Mathias Krause)
- x86: Constify pci_mmcfg_probes[] array (Mathias Krause)
- x86: Mark PCI BIOS initialization code as such (Mathias Krause)
- Parenthesize PCI_DEVID and PCI_VPD_LRDT_ID parameters (Megan Kamiya)
- Remove unnecessary variable in pci_add_dynid() (Tobias Klauser)"
* tag 'pci-v3.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (109 commits)
arm64: dts: Add APM X-Gene PCIe device tree nodes
PCI: Add ACS quirk for AMD A88X southbridge devices
PCI: xgene: Add APM X-Gene PCIe driver
PCI: designware: Remove open-coded bitmap operations
PCI/MSI: Remove unnecessary temporary variable
PCI/MSI: Use __write_msi_msg() instead of write_msi_msg()
MSI/powerpc: Use __read_msi_msg() instead of read_msi_msg()
PCI/MSI: Use __get_cached_msi_msg() instead of get_cached_msi_msg()
PCI/MSI: Add "msi_bus" sysfs MSI/MSI-X control for endpoints
PCI/MSI: Remove "pos" from the struct msi_desc msi_attrib
PCI/MSI: Remove unused kobject from struct msi_desc
PCI/MSI: Rename pci_msi_check_device() to pci_msi_supported()
PCI/MSI: Move D0 check into pci_msi_check_device()
PCI/MSI: Remove arch_msi_check_device()
irqchip: armada-370-xp: Remove arch_msi_check_device()
PCI/MSI/PPC: Remove arch_msi_check_device()
arm64: Add architectural support for PCI
PCI: Add pci_remap_iospace() to map bus I/O resources
of/pci: Add support for parsing PCI host bridge resources from DT
of/pci: Add pci_get_new_domain_nr() and of_get_pci_domain_nr()
...
Conflicts:
arch/arm64/boot/dts/apm-storm.dtsi
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/address.c | 154 | ||||
-rw-r--r-- | drivers/of/of_pci.c | 142 |
2 files changed, 296 insertions, 0 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index e3718250d66e..afdb78299f61 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/module.h> | 5 | #include <linux/module.h> |
6 | #include <linux/of_address.h> | 6 | #include <linux/of_address.h> |
7 | #include <linux/pci_regs.h> | 7 | #include <linux/pci_regs.h> |
8 | #include <linux/sizes.h> | ||
9 | #include <linux/slab.h> | ||
8 | #include <linux/string.h> | 10 | #include <linux/string.h> |
9 | 11 | ||
10 | /* Max address size we deal with */ | 12 | /* Max address size we deal with */ |
@@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, | |||
293 | } | 295 | } |
294 | EXPORT_SYMBOL_GPL(of_pci_range_parser_one); | 296 | EXPORT_SYMBOL_GPL(of_pci_range_parser_one); |
295 | 297 | ||
298 | /* | ||
299 | * of_pci_range_to_resource - Create a resource from an of_pci_range | ||
300 | * @range: the PCI range that describes the resource | ||
301 | * @np: device node where the range belongs to | ||
302 | * @res: pointer to a valid resource that will be updated to | ||
303 | * reflect the values contained in the range. | ||
304 | * | ||
305 | * Returns EINVAL if the range cannot be converted to resource. | ||
306 | * | ||
307 | * Note that if the range is an IO range, the resource will be converted | ||
308 | * using pci_address_to_pio() which can fail if it is called too early or | ||
309 | * if the range cannot be matched to any host bridge IO space (our case here). | ||
310 | * To guard against that we try to register the IO range first. | ||
311 | * If that fails we know that pci_address_to_pio() will do too. | ||
312 | */ | ||
313 | int of_pci_range_to_resource(struct of_pci_range *range, | ||
314 | struct device_node *np, struct resource *res) | ||
315 | { | ||
316 | int err; | ||
317 | res->flags = range->flags; | ||
318 | res->parent = res->child = res->sibling = NULL; | ||
319 | res->name = np->full_name; | ||
320 | |||
321 | if (res->flags & IORESOURCE_IO) { | ||
322 | unsigned long port; | ||
323 | err = pci_register_io_range(range->cpu_addr, range->size); | ||
324 | if (err) | ||
325 | goto invalid_range; | ||
326 | port = pci_address_to_pio(range->cpu_addr); | ||
327 | if (port == (unsigned long)-1) { | ||
328 | err = -EINVAL; | ||
329 | goto invalid_range; | ||
330 | } | ||
331 | res->start = port; | ||
332 | } else { | ||
333 | res->start = range->cpu_addr; | ||
334 | } | ||
335 | res->end = res->start + range->size - 1; | ||
336 | return 0; | ||
337 | |||
338 | invalid_range: | ||
339 | res->start = (resource_size_t)OF_BAD_ADDR; | ||
340 | res->end = (resource_size_t)OF_BAD_ADDR; | ||
341 | return err; | ||
342 | } | ||
296 | #endif /* CONFIG_PCI */ | 343 | #endif /* CONFIG_PCI */ |
297 | 344 | ||
298 | /* | 345 | /* |
@@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, | |||
601 | } | 648 | } |
602 | EXPORT_SYMBOL(of_get_address); | 649 | EXPORT_SYMBOL(of_get_address); |
603 | 650 | ||
651 | #ifdef PCI_IOBASE | ||
652 | struct io_range { | ||
653 | struct list_head list; | ||
654 | phys_addr_t start; | ||
655 | resource_size_t size; | ||
656 | }; | ||
657 | |||
658 | static LIST_HEAD(io_range_list); | ||
659 | static DEFINE_SPINLOCK(io_range_lock); | ||
660 | #endif | ||
661 | |||
662 | /* | ||
663 | * Record the PCI IO range (expressed as CPU physical address + size). | ||
664 | * Return a negative value if an error has occured, zero otherwise | ||
665 | */ | ||
666 | int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) | ||
667 | { | ||
668 | int err = 0; | ||
669 | |||
670 | #ifdef PCI_IOBASE | ||
671 | struct io_range *range; | ||
672 | resource_size_t allocated_size = 0; | ||
673 | |||
674 | /* check if the range hasn't been previously recorded */ | ||
675 | spin_lock(&io_range_lock); | ||
676 | list_for_each_entry(range, &io_range_list, list) { | ||
677 | if (addr >= range->start && addr + size <= range->start + size) { | ||
678 | /* range already registered, bail out */ | ||
679 | goto end_register; | ||
680 | } | ||
681 | allocated_size += range->size; | ||
682 | } | ||
683 | |||
684 | /* range not registed yet, check for available space */ | ||
685 | if (allocated_size + size - 1 > IO_SPACE_LIMIT) { | ||
686 | /* if it's too big check if 64K space can be reserved */ | ||
687 | if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { | ||
688 | err = -E2BIG; | ||
689 | goto end_register; | ||
690 | } | ||
691 | |||
692 | size = SZ_64K; | ||
693 | pr_warn("Requested IO range too big, new size set to 64K\n"); | ||
694 | } | ||
695 | |||
696 | /* add the range to the list */ | ||
697 | range = kzalloc(sizeof(*range), GFP_KERNEL); | ||
698 | if (!range) { | ||
699 | err = -ENOMEM; | ||
700 | goto end_register; | ||
701 | } | ||
702 | |||
703 | range->start = addr; | ||
704 | range->size = size; | ||
705 | |||
706 | list_add_tail(&range->list, &io_range_list); | ||
707 | |||
708 | end_register: | ||
709 | spin_unlock(&io_range_lock); | ||
710 | #endif | ||
711 | |||
712 | return err; | ||
713 | } | ||
714 | |||
715 | phys_addr_t pci_pio_to_address(unsigned long pio) | ||
716 | { | ||
717 | phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; | ||
718 | |||
719 | #ifdef PCI_IOBASE | ||
720 | struct io_range *range; | ||
721 | resource_size_t allocated_size = 0; | ||
722 | |||
723 | if (pio > IO_SPACE_LIMIT) | ||
724 | return address; | ||
725 | |||
726 | spin_lock(&io_range_lock); | ||
727 | list_for_each_entry(range, &io_range_list, list) { | ||
728 | if (pio >= allocated_size && pio < allocated_size + range->size) { | ||
729 | address = range->start + pio - allocated_size; | ||
730 | break; | ||
731 | } | ||
732 | allocated_size += range->size; | ||
733 | } | ||
734 | spin_unlock(&io_range_lock); | ||
735 | #endif | ||
736 | |||
737 | return address; | ||
738 | } | ||
739 | |||
604 | unsigned long __weak pci_address_to_pio(phys_addr_t address) | 740 | unsigned long __weak pci_address_to_pio(phys_addr_t address) |
605 | { | 741 | { |
742 | #ifdef PCI_IOBASE | ||
743 | struct io_range *res; | ||
744 | resource_size_t offset = 0; | ||
745 | unsigned long addr = -1; | ||
746 | |||
747 | spin_lock(&io_range_lock); | ||
748 | list_for_each_entry(res, &io_range_list, list) { | ||
749 | if (address >= res->start && address < res->start + res->size) { | ||
750 | addr = res->start - address + offset; | ||
751 | break; | ||
752 | } | ||
753 | offset += res->size; | ||
754 | } | ||
755 | spin_unlock(&io_range_lock); | ||
756 | |||
757 | return addr; | ||
758 | #else | ||
606 | if (address > IO_SPACE_LIMIT) | 759 | if (address > IO_SPACE_LIMIT) |
607 | return (unsigned long)-1; | 760 | return (unsigned long)-1; |
608 | 761 | ||
609 | return (unsigned long) address; | 762 | return (unsigned long) address; |
763 | #endif | ||
610 | } | 764 | } |
611 | 765 | ||
612 | static int __of_address_to_resource(struct device_node *dev, | 766 | static int __of_address_to_resource(struct device_node *dev, |
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 848199633798..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 | ||
6 | static inline int __of_pci_pci_compare(struct device_node *node, | 8 | static inline int __of_pci_pci_compare(struct device_node *node, |
7 | unsigned int data) | 9 | unsigned int data) |
@@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res) | |||
89 | } | 91 | } |
90 | EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); | 92 | EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); |
91 | 93 | ||
94 | /** | ||
95 | * This function will try to obtain the host bridge domain number by | ||
96 | * finding a property called "linux,pci-domain" of the given device node. | ||
97 | * | ||
98 | * @node: device tree node with the domain information | ||
99 | * | ||
100 | * Returns the associated domain number from DT in the range [0-0xffff], or | ||
101 | * a negative value if the required property is not found. | ||
102 | */ | ||
103 | int of_get_pci_domain_nr(struct device_node *node) | ||
104 | { | ||
105 | const __be32 *value; | ||
106 | int len; | ||
107 | u16 domain; | ||
108 | |||
109 | value = of_get_property(node, "linux,pci-domain", &len); | ||
110 | if (!value || len < sizeof(*value)) | ||
111 | return -EINVAL; | ||
112 | |||
113 | domain = (u16)be32_to_cpup(value); | ||
114 | |||
115 | return domain; | ||
116 | } | ||
117 | EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); | ||
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 | */ | ||
139 | int 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 | |||
225 | conversion_failed: | ||
226 | kfree(res); | ||
227 | parse_failed: | ||
228 | pci_free_resource_list(resources); | ||
229 | return err; | ||
230 | } | ||
231 | EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); | ||
232 | #endif /* CONFIG_OF_ADDRESS */ | ||
233 | |||
92 | #ifdef CONFIG_PCI_MSI | 234 | #ifdef CONFIG_PCI_MSI |
93 | 235 | ||
94 | static LIST_HEAD(of_pci_msi_chip_list); | 236 | static LIST_HEAD(of_pci_msi_chip_list); |