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.3-rc3 290 lines 7.5 kB view raw
1/* 2 * Simple, generic PCI host controller driver targetting firmware-initialised 3 * systems and virtual machines (e.g. the PCI emulation provided by kvmtool). 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Copyright (C) 2014 ARM Limited 18 * 19 * Author: Will Deacon <will.deacon@arm.com> 20 */ 21 22#include <linux/kernel.h> 23#include <linux/module.h> 24#include <linux/of_address.h> 25#include <linux/of_pci.h> 26#include <linux/platform_device.h> 27 28struct gen_pci_cfg_bus_ops { 29 u32 bus_shift; 30 void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int); 31}; 32 33struct gen_pci_cfg_windows { 34 struct resource res; 35 struct resource *bus_range; 36 void __iomem **win; 37 38 const struct gen_pci_cfg_bus_ops *ops; 39}; 40 41/* 42 * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI 43 * sysdata. Add pci_sys_data as the first element in struct gen_pci so 44 * that when we use a gen_pci pointer as sysdata, it is also a pointer to 45 * a struct pci_sys_data. 46 */ 47struct gen_pci { 48#ifdef CONFIG_ARM 49 struct pci_sys_data sys; 50#endif 51 struct pci_host_bridge host; 52 struct gen_pci_cfg_windows cfg; 53 struct list_head resources; 54}; 55 56static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, 57 unsigned int devfn, 58 int where) 59{ 60 struct gen_pci *pci = bus->sysdata; 61 resource_size_t idx = bus->number - pci->cfg.bus_range->start; 62 63 return pci->cfg.win[idx] + ((devfn << 8) | where); 64} 65 66static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = { 67 .bus_shift = 16, 68 .map_bus = gen_pci_map_cfg_bus_cam, 69}; 70 71static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, 72 unsigned int devfn, 73 int where) 74{ 75 struct gen_pci *pci = bus->sysdata; 76 resource_size_t idx = bus->number - pci->cfg.bus_range->start; 77 78 return pci->cfg.win[idx] + ((devfn << 12) | where); 79} 80 81static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { 82 .bus_shift = 20, 83 .map_bus = gen_pci_map_cfg_bus_ecam, 84}; 85 86static struct pci_ops gen_pci_ops = { 87 .read = pci_generic_config_read, 88 .write = pci_generic_config_write, 89}; 90 91static const struct of_device_id gen_pci_of_match[] = { 92 { .compatible = "pci-host-cam-generic", 93 .data = &gen_pci_cfg_cam_bus_ops }, 94 95 { .compatible = "pci-host-ecam-generic", 96 .data = &gen_pci_cfg_ecam_bus_ops }, 97 98 { }, 99}; 100MODULE_DEVICE_TABLE(of, gen_pci_of_match); 101 102static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) 103{ 104 pci_free_resource_list(&pci->resources); 105} 106 107static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) 108{ 109 int err, res_valid = 0; 110 struct device *dev = pci->host.dev.parent; 111 struct device_node *np = dev->of_node; 112 resource_size_t iobase; 113 struct resource_entry *win; 114 115 err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, 116 &iobase); 117 if (err) 118 return err; 119 120 resource_list_for_each_entry(win, &pci->resources) { 121 struct resource *parent, *res = win->res; 122 123 switch (resource_type(res)) { 124 case IORESOURCE_IO: 125 parent = &ioport_resource; 126 err = pci_remap_iospace(res, iobase); 127 if (err) { 128 dev_warn(dev, "error %d: failed to map resource %pR\n", 129 err, res); 130 continue; 131 } 132 break; 133 case IORESOURCE_MEM: 134 parent = &iomem_resource; 135 res_valid |= !(res->flags & IORESOURCE_PREFETCH); 136 break; 137 case IORESOURCE_BUS: 138 pci->cfg.bus_range = res; 139 default: 140 continue; 141 } 142 143 err = devm_request_resource(dev, parent, res); 144 if (err) 145 goto out_release_res; 146 } 147 148 if (!res_valid) { 149 dev_err(dev, "non-prefetchable memory resource required\n"); 150 err = -EINVAL; 151 goto out_release_res; 152 } 153 154 return 0; 155 156out_release_res: 157 gen_pci_release_of_pci_ranges(pci); 158 return err; 159} 160 161static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) 162{ 163 int err; 164 u8 bus_max; 165 resource_size_t busn; 166 struct resource *bus_range; 167 struct device *dev = pci->host.dev.parent; 168 struct device_node *np = dev->of_node; 169 170 err = of_address_to_resource(np, 0, &pci->cfg.res); 171 if (err) { 172 dev_err(dev, "missing \"reg\" property\n"); 173 return err; 174 } 175 176 /* Limit the bus-range to fit within reg */ 177 bus_max = pci->cfg.bus_range->start + 178 (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; 179 pci->cfg.bus_range->end = min_t(resource_size_t, 180 pci->cfg.bus_range->end, bus_max); 181 182 pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range), 183 sizeof(*pci->cfg.win), GFP_KERNEL); 184 if (!pci->cfg.win) 185 return -ENOMEM; 186 187 /* Map our Configuration Space windows */ 188 if (!devm_request_mem_region(dev, pci->cfg.res.start, 189 resource_size(&pci->cfg.res), 190 "Configuration Space")) 191 return -ENOMEM; 192 193 bus_range = pci->cfg.bus_range; 194 for (busn = bus_range->start; busn <= bus_range->end; ++busn) { 195 u32 idx = busn - bus_range->start; 196 u32 sz = 1 << pci->cfg.ops->bus_shift; 197 198 pci->cfg.win[idx] = devm_ioremap(dev, 199 pci->cfg.res.start + busn * sz, 200 sz); 201 if (!pci->cfg.win[idx]) 202 return -ENOMEM; 203 } 204 205 return 0; 206} 207 208static int gen_pci_probe(struct platform_device *pdev) 209{ 210 int err; 211 const char *type; 212 const struct of_device_id *of_id; 213 const int *prop; 214 struct device *dev = &pdev->dev; 215 struct device_node *np = dev->of_node; 216 struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); 217 struct pci_bus *bus, *child; 218 219 if (!pci) 220 return -ENOMEM; 221 222 type = of_get_property(np, "device_type", NULL); 223 if (!type || strcmp(type, "pci")) { 224 dev_err(dev, "invalid \"device_type\" %s\n", type); 225 return -EINVAL; 226 } 227 228 prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL); 229 if (prop) { 230 if (*prop) 231 pci_add_flags(PCI_PROBE_ONLY); 232 else 233 pci_clear_flags(PCI_PROBE_ONLY); 234 } 235 236 of_id = of_match_node(gen_pci_of_match, np); 237 pci->cfg.ops = of_id->data; 238 gen_pci_ops.map_bus = pci->cfg.ops->map_bus; 239 pci->host.dev.parent = dev; 240 INIT_LIST_HEAD(&pci->host.windows); 241 INIT_LIST_HEAD(&pci->resources); 242 243 /* Parse our PCI ranges and request their resources */ 244 err = gen_pci_parse_request_of_pci_ranges(pci); 245 if (err) 246 return err; 247 248 /* Parse and map our Configuration Space windows */ 249 err = gen_pci_parse_map_cfg_windows(pci); 250 if (err) { 251 gen_pci_release_of_pci_ranges(pci); 252 return err; 253 } 254 255 /* Do not reassign resources if probe only */ 256 if (!pci_has_flag(PCI_PROBE_ONLY)) 257 pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); 258 259 bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources); 260 if (!bus) { 261 dev_err(dev, "Scanning rootbus failed"); 262 return -ENODEV; 263 } 264 265 pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); 266 267 if (!pci_has_flag(PCI_PROBE_ONLY)) { 268 pci_bus_size_bridges(bus); 269 pci_bus_assign_resources(bus); 270 271 list_for_each_entry(child, &bus->children, node) 272 pcie_bus_configure_settings(child); 273 } 274 275 pci_bus_add_devices(bus); 276 return 0; 277} 278 279static struct platform_driver gen_pci_driver = { 280 .driver = { 281 .name = "pci-host-generic", 282 .of_match_table = gen_pci_of_match, 283 }, 284 .probe = gen_pci_probe, 285}; 286module_platform_driver(gen_pci_driver); 287 288MODULE_DESCRIPTION("Generic PCI host driver"); 289MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); 290MODULE_LICENSE("GPL v2");