Skip to content

Commit a1aed6b

Browse files
committed
Merge branch 'pci/devtree-create'
- Add device_add_of_node() to set dev->of_node and dev->fwnode only if they haven't been set already (Herve Codina) - Allow of_pci_set_address() to set the DT address property for root bus nodes, where there is no PCI bridge to supply the PCI bus/device/function part of the property (Herve Codina) - Create DT nodes for PCI host bridges to enable loading device tree overlays to create platform devices for PCI devices that have several features that require multiple drivers (Herve Codina) * pci/devtree-create: PCI: of: Create device tree PCI host bridge node PCI: of_property: Constify parameter in of_pci_get_addr_flags() PCI: of_property: Add support for NULL pdev in of_pci_set_address() PCI: of: Use device_{add,remove}_of_node() to attach of_node to existing device driver core: Introduce device_{add,remove}_of_node()
2 parents 38d42a6 + 1f34072 commit a1aed6b

File tree

7 files changed

+300
-7
lines changed

7 files changed

+300
-7
lines changed

drivers/base/core.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5170,6 +5170,67 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
51705170
}
51715171
EXPORT_SYMBOL_GPL(set_secondary_fwnode);
51725172

5173+
/**
5174+
* device_remove_of_node - Remove an of_node from a device
5175+
* @dev: device whose device tree node is being removed
5176+
*/
5177+
void device_remove_of_node(struct device *dev)
5178+
{
5179+
dev = get_device(dev);
5180+
if (!dev)
5181+
return;
5182+
5183+
if (!dev->of_node)
5184+
goto end;
5185+
5186+
if (dev->fwnode == of_fwnode_handle(dev->of_node))
5187+
dev->fwnode = NULL;
5188+
5189+
of_node_put(dev->of_node);
5190+
dev->of_node = NULL;
5191+
5192+
end:
5193+
put_device(dev);
5194+
}
5195+
EXPORT_SYMBOL_GPL(device_remove_of_node);
5196+
5197+
/**
5198+
* device_add_of_node - Add an of_node to an existing device
5199+
* @dev: device whose device tree node is being added
5200+
* @of_node: of_node to add
5201+
*
5202+
* Return: 0 on success or error code on failure.
5203+
*/
5204+
int device_add_of_node(struct device *dev, struct device_node *of_node)
5205+
{
5206+
int ret;
5207+
5208+
if (!of_node)
5209+
return -EINVAL;
5210+
5211+
dev = get_device(dev);
5212+
if (!dev)
5213+
return -EINVAL;
5214+
5215+
if (dev->of_node) {
5216+
dev_err(dev, "Cannot replace node %pOF with %pOF\n",
5217+
dev->of_node, of_node);
5218+
ret = -EBUSY;
5219+
goto end;
5220+
}
5221+
5222+
dev->of_node = of_node_get(of_node);
5223+
5224+
if (!dev->fwnode)
5225+
dev->fwnode = of_fwnode_handle(of_node);
5226+
5227+
ret = 0;
5228+
end:
5229+
put_device(dev);
5230+
return ret;
5231+
}
5232+
EXPORT_SYMBOL_GPL(device_add_of_node);
5233+
51735234
/**
51745235
* device_set_of_node_from_dev - reuse device-tree node of another device
51755236
* @dev: device whose device-tree node is being set

drivers/pci/of.c

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,8 +653,8 @@ void of_pci_remove_node(struct pci_dev *pdev)
653653
np = pci_device_to_OF_node(pdev);
654654
if (!np || !of_node_check_flag(np, OF_DYNAMIC))
655655
return;
656-
pdev->dev.of_node = NULL;
657656

657+
device_remove_of_node(&pdev->dev);
658658
of_changeset_revert(np->data);
659659
of_changeset_destroy(np->data);
660660
of_node_put(np);
@@ -711,11 +711,18 @@ void of_pci_make_dev_node(struct pci_dev *pdev)
711711
goto out_free_node;
712712

713713
np->data = cset;
714-
pdev->dev.of_node = np;
714+
715+
ret = device_add_of_node(&pdev->dev, np);
716+
if (ret)
717+
goto out_revert_cset;
718+
715719
kfree(name);
716720

717721
return;
718722

723+
out_revert_cset:
724+
np->data = NULL;
725+
of_changeset_revert(cset);
719726
out_free_node:
720727
of_node_put(np);
721728
out_destroy_cset:
@@ -724,7 +731,112 @@ void of_pci_make_dev_node(struct pci_dev *pdev)
724731
out_free_name:
725732
kfree(name);
726733
}
727-
#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 */
728840

729841
/**
730842
* of_pci_supply_present() - Check if the power supply is present for the PCI

drivers/pci/of_property.c

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@ enum of_pci_prop_compatible {
5454
static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,
5555
u32 reg_num, u32 flags, bool reloc)
5656
{
57-
prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) |
58-
FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) |
59-
FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn));
57+
if (pdev) {
58+
prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) |
59+
FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) |
60+
FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn));
61+
} else
62+
prop[0] = 0;
63+
6064
prop[0] |= flags | reg_num;
6165
if (!reloc) {
6266
prop[0] |= OF_PCI_ADDR_FIELD_NONRELOC;
@@ -65,7 +69,7 @@ static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,
6569
}
6670
}
6771

68-
static int of_pci_get_addr_flags(struct resource *res, u32 *flags)
72+
static int of_pci_get_addr_flags(const struct resource *res, u32 *flags)
6973
{
7074
u32 ss;
7175

@@ -390,3 +394,106 @@ int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
390394

391395
return 0;
392396
}
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
@@ -944,9 +944,16 @@ void of_pci_make_dev_node(struct pci_dev *pdev);
944944
void of_pci_remove_node(struct pci_dev *pdev);
945945
int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
946946
struct device_node *np);
947+
void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge);
948+
void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge);
949+
int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge,
950+
struct of_changeset *ocs,
951+
struct device_node *np);
947952
#else
948953
static inline void of_pci_make_dev_node(struct pci_dev *pdev) { }
949954
static inline void of_pci_remove_node(struct pci_dev *pdev) { }
955+
static inline void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) { }
956+
static inline void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) { }
950957
#endif
951958

952959
#ifdef CONFIG_PCIEAER

drivers/pci/probe.c

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

1101+
of_pci_make_host_bridge_node(bridge);
1102+
11011103
down_write(&pci_bus_sem);
11021104
list_add_tail(&bus->node, &pci_root_buses);
11031105
up_write(&pci_bus_sem);

drivers/pci/remove.c

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

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

include/linux/device.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,8 @@ int device_online(struct device *dev);
11911191
void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
11921192
void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
11931193
void device_set_node(struct device *dev, struct fwnode_handle *fwnode);
1194+
int device_add_of_node(struct device *dev, struct device_node *of_node);
1195+
void device_remove_of_node(struct device *dev);
11941196
void device_set_of_node_from_dev(struct device *dev, const struct device *dev2);
11951197

11961198
static inline struct device_node *dev_of_node(struct device *dev)

0 commit comments

Comments
 (0)