Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.6-rc2 194 lines 5.0 kB view raw
1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 * 6 * This program is distributed in the hope that it will be useful, 7 * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 * GNU General Public License for more details. 10 * 11 * You should have received a copy of the GNU General Public License 12 * along with this program. If not, see <http://www.gnu.org/licenses/>. 13 * 14 * Copyright (C) 2014 ARM Limited 15 * 16 * Author: Will Deacon <will.deacon@arm.com> 17 */ 18 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/of_address.h> 22#include <linux/of_pci.h> 23#include <linux/platform_device.h> 24 25#include "pci-host-common.h" 26 27static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) 28{ 29 pci_free_resource_list(&pci->resources); 30} 31 32static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) 33{ 34 int err, res_valid = 0; 35 struct device *dev = pci->host.dev.parent; 36 struct device_node *np = dev->of_node; 37 resource_size_t iobase; 38 struct resource_entry *win; 39 40 err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, 41 &iobase); 42 if (err) 43 return err; 44 45 resource_list_for_each_entry(win, &pci->resources) { 46 struct resource *parent, *res = win->res; 47 48 switch (resource_type(res)) { 49 case IORESOURCE_IO: 50 parent = &ioport_resource; 51 err = pci_remap_iospace(res, iobase); 52 if (err) { 53 dev_warn(dev, "error %d: failed to map resource %pR\n", 54 err, res); 55 continue; 56 } 57 break; 58 case IORESOURCE_MEM: 59 parent = &iomem_resource; 60 res_valid |= !(res->flags & IORESOURCE_PREFETCH); 61 break; 62 case IORESOURCE_BUS: 63 pci->cfg.bus_range = res; 64 default: 65 continue; 66 } 67 68 err = devm_request_resource(dev, parent, res); 69 if (err) 70 goto out_release_res; 71 } 72 73 if (!res_valid) { 74 dev_err(dev, "non-prefetchable memory resource required\n"); 75 err = -EINVAL; 76 goto out_release_res; 77 } 78 79 return 0; 80 81out_release_res: 82 gen_pci_release_of_pci_ranges(pci); 83 return err; 84} 85 86static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) 87{ 88 int err; 89 u8 bus_max; 90 resource_size_t busn; 91 struct resource *bus_range; 92 struct device *dev = pci->host.dev.parent; 93 struct device_node *np = dev->of_node; 94 u32 sz = 1 << pci->cfg.ops->bus_shift; 95 96 err = of_address_to_resource(np, 0, &pci->cfg.res); 97 if (err) { 98 dev_err(dev, "missing \"reg\" property\n"); 99 return err; 100 } 101 102 /* Limit the bus-range to fit within reg */ 103 bus_max = pci->cfg.bus_range->start + 104 (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; 105 pci->cfg.bus_range->end = min_t(resource_size_t, 106 pci->cfg.bus_range->end, bus_max); 107 108 pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range), 109 sizeof(*pci->cfg.win), GFP_KERNEL); 110 if (!pci->cfg.win) 111 return -ENOMEM; 112 113 /* Map our Configuration Space windows */ 114 if (!devm_request_mem_region(dev, pci->cfg.res.start, 115 resource_size(&pci->cfg.res), 116 "Configuration Space")) 117 return -ENOMEM; 118 119 bus_range = pci->cfg.bus_range; 120 for (busn = bus_range->start; busn <= bus_range->end; ++busn) { 121 u32 idx = busn - bus_range->start; 122 123 pci->cfg.win[idx] = devm_ioremap(dev, 124 pci->cfg.res.start + idx * sz, 125 sz); 126 if (!pci->cfg.win[idx]) 127 return -ENOMEM; 128 } 129 130 return 0; 131} 132 133int pci_host_common_probe(struct platform_device *pdev, 134 struct gen_pci *pci) 135{ 136 int err; 137 const char *type; 138 struct device *dev = &pdev->dev; 139 struct device_node *np = dev->of_node; 140 struct pci_bus *bus, *child; 141 142 type = of_get_property(np, "device_type", NULL); 143 if (!type || strcmp(type, "pci")) { 144 dev_err(dev, "invalid \"device_type\" %s\n", type); 145 return -EINVAL; 146 } 147 148 of_pci_check_probe_only(); 149 150 pci->host.dev.parent = dev; 151 INIT_LIST_HEAD(&pci->host.windows); 152 INIT_LIST_HEAD(&pci->resources); 153 154 /* Parse our PCI ranges and request their resources */ 155 err = gen_pci_parse_request_of_pci_ranges(pci); 156 if (err) 157 return err; 158 159 /* Parse and map our Configuration Space windows */ 160 err = gen_pci_parse_map_cfg_windows(pci); 161 if (err) { 162 gen_pci_release_of_pci_ranges(pci); 163 return err; 164 } 165 166 /* Do not reassign resources if probe only */ 167 if (!pci_has_flag(PCI_PROBE_ONLY)) 168 pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); 169 170 171 bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start, 172 &pci->cfg.ops->ops, pci, &pci->resources); 173 if (!bus) { 174 dev_err(dev, "Scanning rootbus failed"); 175 return -ENODEV; 176 } 177 178 pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); 179 180 if (!pci_has_flag(PCI_PROBE_ONLY)) { 181 pci_bus_size_bridges(bus); 182 pci_bus_assign_resources(bus); 183 184 list_for_each_entry(child, &bus->children, node) 185 pcie_bus_configure_settings(child); 186 } 187 188 pci_bus_add_devices(bus); 189 return 0; 190} 191 192MODULE_DESCRIPTION("Generic PCI host driver common code"); 193MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); 194MODULE_LICENSE("GPL v2");