On 06/27/2014 07:49 AM, Will Deacon wrote: > On Fri, Jun 27, 2014 at 12:03:34PM +0100, Arnd Bergmann wrote: >> On Thursday 26 June 2014 19:44:21 Rob Herring wrote: >>> I don't agree arm32 is harder than microblaze. Yes, converting ALL of >>> arm would be, but that is not necessary. With Liviu's latest branch >>> the hacks I previously needed are gone (thanks!), and this is all I >>> need to get Versatile PCI working (under QEMU): >> >> I meant converting all of arm32 would be harder, but I agree we don't >> have to do it. It would be nice to convert all of the drivers/pci/host >> drivers though, iow all multiplatform-enabled ones, and leaving the >> current arm32 pci implementation for the platforms we don't want to >> convert to multiplatform anyway (footbridge, iop, ixp4xx, ks8695 (?), >> pxa, sa1100). > > I'm more than happy to convert the generic host controller we merged > recently, but I'd probably want the core changes merged first so that I > know I'm not wasting my time! Something like this untested patch... Another issue I found still present is pci_ioremap_io needs some work to unify with the arm implementation. That's a matter of changing the function from an offset to i/o resource. That should be a pretty mechanical change. Also, there is a potential for memory leak because there is no undo for of_create_pci_host_bridge. Rob 8<------------------------------------------------------------------ >From 301b402631b2867ced38d5533586da0bd888045c Mon Sep 17 00:00:00 2001 From: Rob Herring <robh@xxxxxxxxxx> Date: Fri, 27 Jun 2014 09:46:09 -0500 Subject: [PATCH] pci: generic-host: refactor to use common range parsing Signed-off-by: Rob Herring <robh@xxxxxxxxxx> --- drivers/pci/host/pci-host-generic.c | 191 ++++++------------------------------ 1 file changed, 29 insertions(+), 162 deletions(-) diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 44fe6aa..57fd1e1 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -32,16 +32,14 @@ struct gen_pci_cfg_bus_ops { struct gen_pci_cfg_windows { struct resource res; - struct resource bus_range; void __iomem **win; const struct gen_pci_cfg_bus_ops *ops; }; struct gen_pci { - struct pci_host_bridge host; + struct pci_host_bridge *host; struct gen_pci_cfg_windows cfg; - struct list_head resources; }; static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, @@ -50,7 +48,7 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, { struct pci_sys_data *sys = bus->sysdata; struct gen_pci *pci = sys->private_data; - resource_size_t idx = bus->number - pci->cfg.bus_range.start; + resource_size_t idx = bus->number - bus->busn_res.start; return pci->cfg.win[idx] + ((devfn << 8) | where); } @@ -66,7 +64,7 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, { struct pci_sys_data *sys = bus->sysdata; struct gen_pci *pci = sys->private_data; - resource_size_t idx = bus->number - pci->cfg.bus_range.start; + resource_size_t idx = bus->number - bus->busn_res.start; return pci->cfg.win[idx] + ((devfn << 12) | where); } @@ -138,154 +136,32 @@ static const struct of_device_id gen_pci_of_match[] = { }; MODULE_DEVICE_TABLE(of, gen_pci_of_match); -static int gen_pci_calc_io_offset(struct device *dev, - struct of_pci_range *range, - struct resource *res, - resource_size_t *offset) -{ - static atomic_t wins = ATOMIC_INIT(0); - int err, idx, max_win; - unsigned int window; - - if (!PAGE_ALIGNED(range->cpu_addr)) - return -EINVAL; - - max_win = (IO_SPACE_LIMIT + 1) / SZ_64K; - idx = atomic_inc_return(&wins); - if (idx > max_win) - return -ENOSPC; - - window = (idx - 1) * SZ_64K; - err = pci_ioremap_io(window, range->cpu_addr); - if (err) - return err; - - of_pci_range_to_resource(range, dev->of_node, res); - res->start = window; - res->end = res->start + range->size - 1; - *offset = window - range->pci_addr; - return 0; -} - -static int gen_pci_calc_mem_offset(struct device *dev, - struct of_pci_range *range, - struct resource *res, - resource_size_t *offset) -{ - of_pci_range_to_resource(range, dev->of_node, res); - *offset = range->cpu_addr - range->pci_addr; - return 0; -} - -static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) -{ - struct pci_host_bridge_window *win; - - list_for_each_entry(win, &pci->resources, list) - release_resource(win->res); - - pci_free_resource_list(&pci->resources); -} - -static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - int err, res_valid = 0; - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; - - if (of_pci_range_parser_init(&parser, np)) { - dev_err(dev, "missing \"ranges\" property\n"); - return -EINVAL; - } - - for_each_of_pci_range(&parser, &range) { - struct resource *parent, *res; - resource_size_t offset; - u32 restype = range.flags & IORESOURCE_TYPE_BITS; - - res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL); - if (!res) { - err = -ENOMEM; - goto out_release_res; - } - - switch (restype) { - case IORESOURCE_IO: - parent = &ioport_resource; - err = gen_pci_calc_io_offset(dev, &range, res, &offset); - break; - case IORESOURCE_MEM: - parent = &iomem_resource; - err = gen_pci_calc_mem_offset(dev, &range, res, &offset); - res_valid |= !(res->flags & IORESOURCE_PREFETCH || err); - break; - default: - err = -EINVAL; - continue; - } - - if (err) { - dev_warn(dev, - "error %d: failed to add resource [type 0x%x, %lld bytes]\n", - err, restype, range.size); - continue; - } - - err = request_resource(parent, res); - if (err) - goto out_release_res; - - pci_add_resource_offset(&pci->resources, res, offset); - } - - if (!res_valid) { - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - goto out_release_res; - } - - return 0; - -out_release_res: - gen_pci_release_of_pci_ranges(pci); - return err; -} - static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) { int err; u8 bus_max; resource_size_t busn; - struct resource *bus_range; - struct device *dev = pci->host.dev.parent; + struct pci_bus *bus = pci->host->bus; + struct resource *bus_range = &bus->busn_res; + struct device *dev = pci->host->dev.parent; struct device_node *np = dev->of_node; - if (of_pci_parse_bus_range(np, &pci->cfg.bus_range)) - pci->cfg.bus_range = (struct resource) { - .name = np->name, - .start = 0, - .end = 0xff, - .flags = IORESOURCE_BUS, - }; - err = of_address_to_resource(np, 0, &pci->cfg.res); if (err) { dev_err(dev, "missing \"reg\" property\n"); return err; } - pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range), + pci->cfg.win = devm_kcalloc(dev, resource_size(bus_range), sizeof(*pci->cfg.win), GFP_KERNEL); if (!pci->cfg.win) return -ENOMEM; /* Limit the bus-range to fit within reg */ - bus_max = pci->cfg.bus_range.start + + bus_max = bus_range->start + (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; - pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end, - bus_max); + pci_bus_update_busn_res_end(bus, min_t(resource_size_t, + bus_range->end, bus_max)); /* Map our Configuration Space windows */ if (!devm_request_mem_region(dev, pci->cfg.res.start, @@ -293,7 +169,6 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) "Configuration Space")) return -ENOMEM; - bus_range = &pci->cfg.bus_range; for (busn = bus_range->start; busn <= bus_range->end; ++busn) { u32 idx = busn - bus_range->start; u32 sz = 1 << pci->cfg.ops->bus_shift; @@ -305,34 +180,21 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) return -ENOMEM; } - /* Register bus resource */ - pci_add_resource(&pci->resources, bus_range); return 0; } -static int gen_pci_setup(int nr, struct pci_sys_data *sys) -{ - struct gen_pci *pci = sys->private_data; - list_splice_init(&pci->resources, &sys->resources); - return 1; -} +/* Unused, temporary to satisfy ARM arch code */ +static struct pci_sys_data sys; static int gen_pci_probe(struct platform_device *pdev) { - int err; + int err, lastbus; const char *type; const struct of_device_id *of_id; const int *prop; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - struct hw_pci hw = { - .nr_controllers = 1, - .private_data = (void **)&pci, - .setup = gen_pci_setup, - .map_irq = of_irq_parse_and_map_pci, - .ops = &gen_pci_ops, - }; if (!pci) return -ENOMEM; @@ -353,23 +215,28 @@ static int gen_pci_probe(struct platform_device *pdev) of_id = of_match_node(gen_pci_of_match, np); pci->cfg.ops = of_id->data; - pci->host.dev.parent = dev; - INIT_LIST_HEAD(&pci->host.windows); - INIT_LIST_HEAD(&pci->resources); - /* Parse our PCI ranges and request their resources */ - err = gen_pci_parse_request_of_pci_ranges(pci); - if (err) - return err; + pci->host = of_create_pci_host_bridge(&pdev->dev, &gen_pci_ops, &sys); + if (PTR_ERR(pci->host)) + return PTR_ERR(pci->host); /* Parse and map our Configuration Space windows */ err = gen_pci_parse_map_cfg_windows(pci); - if (err) { - gen_pci_release_of_pci_ranges(pci); + if (err) return err; - } - pci_common_init_dev(dev, &hw); + pci_ioremap_io(0, pci->host->io_base); + + pci_add_flags(PCI_ENABLE_PROC_DOMAINS); + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC); + + lastbus = pci_scan_child_bus(pci->host->bus); + pci_bus_update_busn_res_end(pci->host->bus, lastbus); + + pci_assign_unassigned_bus_resources(pci->host->bus); + + pci_bus_add_devices(pci->host->bus); + return 0; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html