diff options
Diffstat (limited to 'drivers/pci/of.c')
-rw-r--r-- | drivers/pci/of.c | 149 |
1 files changed, 131 insertions, 18 deletions
diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 52f770bcc481..ab7a8252bf41 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -190,7 +190,8 @@ EXPORT_SYMBOL_GPL(of_pci_get_devfn); * * Returns 0 on success or a negative error-code on failure. */ -int of_pci_parse_bus_range(struct device_node *node, struct resource *res) +static int of_pci_parse_bus_range(struct device_node *node, + struct resource *res) { u32 bus_range[2]; int error; @@ -207,7 +208,6 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res) return 0; } -EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); /** * of_get_pci_domain_nr - Find the host bridge domain number @@ -302,8 +302,6 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_only); * devm_of_pci_get_host_bridge_resources() - Resource-managed parsing of PCI * host bridge resources from DT * @dev: host bridge device - * @busno: bus number associated with the bridge root bus - * @bus_max: maximum number of buses for this bridge * @resources: list where the range of resources will be added after DT parsing * @ib_resources: list where the range of inbound resources (with addresses * from 'dma-ranges') will be added after DT parsing @@ -319,7 +317,6 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_only); * value if it failed. */ static int devm_of_pci_get_host_bridge_resources(struct device *dev, - unsigned char busno, unsigned char bus_max, struct list_head *resources, struct list_head *ib_resources, resource_size_t *io_base) @@ -343,14 +340,15 @@ static int devm_of_pci_get_host_bridge_resources(struct device *dev, err = of_pci_parse_bus_range(dev_node, bus_range); if (err) { - bus_range->start = busno; - bus_range->end = bus_max; + bus_range->start = 0; + bus_range->end = 0xff; bus_range->flags = IORESOURCE_BUS; - dev_info(dev, " No bus range found for %pOF, using %pR\n", - dev_node, bus_range); } else { - if (bus_range->end > bus_range->start + bus_max) - bus_range->end = bus_range->start + bus_max; + if (bus_range->end > 0xff) { + dev_warn(dev, " Invalid end bus number in %pR, defaulting to 0xff\n", + bus_range); + bus_range->end = 0xff; + } } pci_add_resource(resources, bus_range); @@ -457,9 +455,9 @@ failed: * @out_irq: structure of_phandle_args filled by this function * * This function resolves the PCI interrupt for a given PCI device. If a - * device-node exists for a given pci_dev, it will use normal OF tree + * device node exists for a given pci_dev, it will use normal OF tree * walking. If not, it will implement standard swizzling and walk up the - * PCI tree until an device-node is found, at which point it will finish + * PCI tree until a device node is found, at which point it will finish * resolving using the OF tree walking. */ static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) @@ -519,13 +517,16 @@ static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args * } /* - * Ok, we have found a parent with a device-node, hand over to + * Ok, we have found a parent with a device node, hand over to * the OF parsing code. + * * We build a unit address from the linux device to be used for * resolution. Note that we use the linux bus number which may * not match your firmware bus numbering. + * * Fortunately, in most cases, interrupt-map-mask doesn't * include the bus number as part of the matching. + * * You should still be careful about that though if you intend * to rely on this function (you ship a firmware that doesn't * create device nodes for all PCI devices). @@ -597,7 +598,7 @@ static int pci_parse_request_of_pci_ranges(struct device *dev, INIT_LIST_HEAD(&bridge->windows); INIT_LIST_HEAD(&bridge->dma_ranges); - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &bridge->windows, + err = devm_of_pci_get_host_bridge_resources(dev, &bridge->windows, &bridge->dma_ranges, &iobase); if (err) return err; @@ -655,8 +656,8 @@ void of_pci_remove_node(struct pci_dev *pdev) np = pci_device_to_OF_node(pdev); if (!np || !of_node_check_flag(np, OF_DYNAMIC)) return; - pdev->dev.of_node = NULL; + device_remove_of_node(&pdev->dev); of_changeset_revert(np->data); of_changeset_destroy(np->data); of_node_put(np); @@ -713,11 +714,18 @@ void of_pci_make_dev_node(struct pci_dev *pdev) goto out_free_node; np->data = cset; - pdev->dev.of_node = np; + + ret = device_add_of_node(&pdev->dev, np); + if (ret) + goto out_revert_cset; + kfree(name); return; +out_revert_cset: + np->data = NULL; + of_changeset_revert(cset); out_free_node: of_node_put(np); out_destroy_cset: @@ -726,7 +734,112 @@ out_destroy_cset: out_free_name: kfree(name); } -#endif + +void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) +{ + struct device_node *np; + + np = pci_bus_to_OF_node(bridge->bus); + if (!np || !of_node_check_flag(np, OF_DYNAMIC)) + return; + + device_remove_of_node(&bridge->bus->dev); + device_remove_of_node(&bridge->dev); + of_changeset_revert(np->data); + of_changeset_destroy(np->data); + of_node_put(np); +} + +void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) +{ + struct device_node *np = NULL; + struct of_changeset *cset; + const char *name; + int ret; + + /* + * If there is already a device tree node linked to the PCI bus handled + * by this bridge (i.e. the PCI root bus), nothing to do. + */ + if (pci_bus_to_OF_node(bridge->bus)) + return; + + /* + * The root bus has no node. Check that the host bridge has no node + * too + */ + if (bridge->dev.of_node) { + dev_err(&bridge->dev, "PCI host bridge of_node already set"); + return; + } + + /* Check if there is a DT root node to attach the created node */ + if (!of_root) { + pr_err("of_root node is NULL, cannot create PCI host bridge node\n"); + return; + } + + name = kasprintf(GFP_KERNEL, "pci@%x,%x", pci_domain_nr(bridge->bus), + bridge->bus->number); + if (!name) + return; + + cset = kmalloc(sizeof(*cset), GFP_KERNEL); + if (!cset) + goto out_free_name; + of_changeset_init(cset); + + np = of_changeset_create_node(cset, of_root, name); + if (!np) + goto out_destroy_cset; + + ret = of_pci_add_host_bridge_properties(bridge, cset, np); + if (ret) + goto out_free_node; + + /* + * This of_node will be added to an existing device. The of_node parent + * is the root OF node and so this node will be handled by the platform + * bus. Avoid any new device creation. + */ + of_node_set_flag(np, OF_POPULATED); + np->fwnode.dev = &bridge->dev; + fwnode_dev_initialized(&np->fwnode, true); + + ret = of_changeset_apply(cset); + if (ret) + goto out_free_node; + + np->data = cset; + + /* Add the of_node to host bridge and the root bus */ + ret = device_add_of_node(&bridge->dev, np); + if (ret) + goto out_revert_cset; + + ret = device_add_of_node(&bridge->bus->dev, np); + if (ret) + goto out_remove_bridge_dev_of_node; + + kfree(name); + + return; + +out_remove_bridge_dev_of_node: + device_remove_of_node(&bridge->dev); +out_revert_cset: + np->data = NULL; + of_changeset_revert(cset); +out_free_node: + of_node_put(np); +out_destroy_cset: + of_changeset_destroy(cset); + kfree(cset); +out_free_name: + kfree(name); +} + +#endif /* CONFIG_PCI_DYNAMIC_OF_NODES */ /** * of_pci_supply_present() - Check if the power supply is present for the PCI |