Skip to content

Commit 1f34072

Browse files
hcodinabjorn-helgaas
authored andcommitted
PCI: of: Create device tree PCI host bridge node
PCI devices device tree nodes can be already created. This was introduced by commit 407d1a5 ("PCI: Create device tree node for bridge"). In order to have device tree nodes related to PCI devices attached on their PCI root bus (the PCI bus handled by the PCI host bridge), a PCI root bus device tree node is needed. This root bus node will be used as the parent node of the first level devices scanned on the bus. On device tree based systems, this PCI root bus device tree node is set to the node of the related PCI host bridge. The PCI host bridge node is available in the device tree used to describe the hardware passed at boot. On non device tree based system (such as ACPI), a device tree node for the PCI host bridge or for the root bus does not exist. Indeed, the PCI host bridge is not described in a device tree used at boot simply because no device tree is passed at boot. The device tree PCI host bridge node creation needs to be done at runtime. This is done in the same way as for the creation of the PCI device nodes. I.e. node and properties are created based on computed information done by the PCI core. Also, as is done on device tree based systems, this PCI host bridge node is used for the PCI root bus. With this done, hardware available in a PCI device that doesn't follow the PCI model consisting in one PCI function handled by one driver can be described by a device tree overlay loaded by the PCI device driver on non device tree based systems. Those PCI devices provide a single PCI function that includes several functionalities that require different drivers. The device tree overlay describes the internal devices and their relationships. It allows to load drivers needed by those different devices in order to have functionalities handled. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Herve Codina <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Rob Herring (Arm) <[email protected]>
1 parent 3dc8ade commit 1f34072

File tree

5 files changed

+220
-1
lines changed

5 files changed

+220
-1
lines changed

drivers/pci/of.c

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,112 @@ void of_pci_make_dev_node(struct pci_dev *pdev)
731731
out_free_name:
732732
kfree(name);
733733
}
734-
#endif
734+
735+
void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge)
736+
{
737+
struct device_node *np;
738+
739+
np = pci_bus_to_OF_node(bridge->bus);
740+
if (!np || !of_node_check_flag(np, OF_DYNAMIC))
741+
return;
742+
743+
device_remove_of_node(&bridge->bus->dev);
744+
device_remove_of_node(&bridge->dev);
745+
of_changeset_revert(np->data);
746+
of_changeset_destroy(np->data);
747+
of_node_put(np);
748+
}
749+
750+
void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge)
751+
{
752+
struct device_node *np = NULL;
753+
struct of_changeset *cset;
754+
const char *name;
755+
int ret;
756+
757+
/*
758+
* If there is already a device tree node linked to the PCI bus handled
759+
* by this bridge (i.e. the PCI root bus), nothing to do.
760+
*/
761+
if (pci_bus_to_OF_node(bridge->bus))
762+
return;
763+
764+
/*
765+
* The root bus has no node. Check that the host bridge has no node
766+
* too
767+
*/
768+
if (bridge->dev.of_node) {
769+
dev_err(&bridge->dev, "PCI host bridge of_node already set");
770+
return;
771+
}
772+
773+
/* Check if there is a DT root node to attach the created node */
774+
if (!of_root) {
775+
pr_err("of_root node is NULL, cannot create PCI host bridge node\n");
776+
return;
777+
}
778+
779+
name = kasprintf(GFP_KERNEL, "pci@%x,%x", pci_domain_nr(bridge->bus),
780+
bridge->bus->number);
781+
if (!name)
782+
return;
783+
784+
cset = kmalloc(sizeof(*cset), GFP_KERNEL);
785+
if (!cset)
786+
goto out_free_name;
787+
of_changeset_init(cset);
788+
789+
np = of_changeset_create_node(cset, of_root, name);
790+
if (!np)
791+
goto out_destroy_cset;
792+
793+
ret = of_pci_add_host_bridge_properties(bridge, cset, np);
794+
if (ret)
795+
goto out_free_node;
796+
797+
/*
798+
* This of_node will be added to an existing device. The of_node parent
799+
* is the root OF node and so this node will be handled by the platform
800+
* bus. Avoid any new device creation.
801+
*/
802+
of_node_set_flag(np, OF_POPULATED);
803+
np->fwnode.dev = &bridge->dev;
804+
fwnode_dev_initialized(&np->fwnode, true);
805+
806+
ret = of_changeset_apply(cset);
807+
if (ret)
808+
goto out_free_node;
809+
810+
np->data = cset;
811+
812+
/* Add the of_node to host bridge and the root bus */
813+
ret = device_add_of_node(&bridge->dev, np);
814+
if (ret)
815+
goto out_revert_cset;
816+
817+
ret = device_add_of_node(&bridge->bus->dev, np);
818+
if (ret)
819+
goto out_remove_bridge_dev_of_node;
820+
821+
kfree(name);
822+
823+
return;
824+
825+
out_remove_bridge_dev_of_node:
826+
device_remove_of_node(&bridge->dev);
827+
out_revert_cset:
828+
np->data = NULL;
829+
of_changeset_revert(cset);
830+
out_free_node:
831+
of_node_put(np);
832+
out_destroy_cset:
833+
of_changeset_destroy(cset);
834+
kfree(cset);
835+
out_free_name:
836+
kfree(name);
837+
}
838+
839+
#endif /* CONFIG_PCI_DYNAMIC_OF_NODES */
735840

736841
/**
737842
* of_pci_supply_present() - Check if the power supply is present for the PCI

drivers/pci/of_property.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,106 @@ int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
394394

395395
return 0;
396396
}
397+
398+
static bool of_pci_is_range_resource(const struct resource *res, u32 *flags)
399+
{
400+
if (!(resource_type(res) & IORESOURCE_MEM) &&
401+
!(resource_type(res) & IORESOURCE_MEM_64))
402+
return false;
403+
404+
if (of_pci_get_addr_flags(res, flags))
405+
return false;
406+
407+
return true;
408+
}
409+
410+
static int of_pci_host_bridge_prop_ranges(struct pci_host_bridge *bridge,
411+
struct of_changeset *ocs,
412+
struct device_node *np)
413+
{
414+
struct resource_entry *window;
415+
unsigned int ranges_sz = 0;
416+
unsigned int n_range = 0;
417+
struct resource *res;
418+
int n_addr_cells;
419+
u32 *ranges;
420+
u64 val64;
421+
u32 flags;
422+
int ret;
423+
424+
n_addr_cells = of_n_addr_cells(np);
425+
if (n_addr_cells <= 0 || n_addr_cells > 2)
426+
return -EINVAL;
427+
428+
resource_list_for_each_entry(window, &bridge->windows) {
429+
res = window->res;
430+
if (!of_pci_is_range_resource(res, &flags))
431+
continue;
432+
n_range++;
433+
}
434+
435+
if (!n_range)
436+
return 0;
437+
438+
ranges = kcalloc(n_range,
439+
(OF_PCI_ADDRESS_CELLS + OF_PCI_SIZE_CELLS +
440+
n_addr_cells) * sizeof(*ranges),
441+
GFP_KERNEL);
442+
if (!ranges)
443+
return -ENOMEM;
444+
445+
resource_list_for_each_entry(window, &bridge->windows) {
446+
res = window->res;
447+
if (!of_pci_is_range_resource(res, &flags))
448+
continue;
449+
450+
/* PCI bus address */
451+
val64 = res->start;
452+
of_pci_set_address(NULL, &ranges[ranges_sz],
453+
val64 - window->offset, 0, flags, false);
454+
ranges_sz += OF_PCI_ADDRESS_CELLS;
455+
456+
/* Host bus address */
457+
if (n_addr_cells == 2)
458+
ranges[ranges_sz++] = upper_32_bits(val64);
459+
ranges[ranges_sz++] = lower_32_bits(val64);
460+
461+
/* Size */
462+
val64 = resource_size(res);
463+
ranges[ranges_sz] = upper_32_bits(val64);
464+
ranges[ranges_sz + 1] = lower_32_bits(val64);
465+
ranges_sz += OF_PCI_SIZE_CELLS;
466+
}
467+
468+
ret = of_changeset_add_prop_u32_array(ocs, np, "ranges", ranges,
469+
ranges_sz);
470+
kfree(ranges);
471+
return ret;
472+
}
473+
474+
int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge,
475+
struct of_changeset *ocs,
476+
struct device_node *np)
477+
{
478+
int ret;
479+
480+
ret = of_changeset_add_prop_string(ocs, np, "device_type", "pci");
481+
if (ret)
482+
return ret;
483+
484+
ret = of_changeset_add_prop_u32(ocs, np, "#address-cells",
485+
OF_PCI_ADDRESS_CELLS);
486+
if (ret)
487+
return ret;
488+
489+
ret = of_changeset_add_prop_u32(ocs, np, "#size-cells",
490+
OF_PCI_SIZE_CELLS);
491+
if (ret)
492+
return ret;
493+
494+
ret = of_pci_host_bridge_prop_ranges(bridge, ocs, np);
495+
if (ret)
496+
return ret;
497+
498+
return 0;
499+
}

drivers/pci/pci.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,9 +876,16 @@ void of_pci_make_dev_node(struct pci_dev *pdev);
876876
void of_pci_remove_node(struct pci_dev *pdev);
877877
int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
878878
struct device_node *np);
879+
void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge);
880+
void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge);
881+
int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge,
882+
struct of_changeset *ocs,
883+
struct device_node *np);
879884
#else
880885
static inline void of_pci_make_dev_node(struct pci_dev *pdev) { }
881886
static inline void of_pci_remove_node(struct pci_dev *pdev) { }
887+
static inline void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) { }
888+
static inline void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) { }
882889
#endif
883890

884891
#ifdef CONFIG_PCIEAER

drivers/pci/probe.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
10941094
dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
10951095
}
10961096

1097+
of_pci_make_host_bridge_node(bridge);
1098+
10971099
down_write(&pci_bus_sem);
10981100
list_add_tail(&bus->node, &pci_root_buses);
10991101
up_write(&pci_bus_sem);

drivers/pci/remove.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ void pci_stop_root_bus(struct pci_bus *bus)
163163
&bus->devices, bus_list)
164164
pci_stop_bus_device(child);
165165

166+
of_pci_remove_host_bridge_node(host_bridge);
167+
166168
/* stop the host bridge */
167169
device_release_driver(&host_bridge->dev);
168170
}

0 commit comments

Comments
 (0)