mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00
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()
This commit is contained in:
commit
a1aed6b34f
|
@ -5170,6 +5170,67 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(set_secondary_fwnode);
|
||||
|
||||
/**
|
||||
* device_remove_of_node - Remove an of_node from a device
|
||||
* @dev: device whose device tree node is being removed
|
||||
*/
|
||||
void device_remove_of_node(struct device *dev)
|
||||
{
|
||||
dev = get_device(dev);
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (!dev->of_node)
|
||||
goto end;
|
||||
|
||||
if (dev->fwnode == of_fwnode_handle(dev->of_node))
|
||||
dev->fwnode = NULL;
|
||||
|
||||
of_node_put(dev->of_node);
|
||||
dev->of_node = NULL;
|
||||
|
||||
end:
|
||||
put_device(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_remove_of_node);
|
||||
|
||||
/**
|
||||
* device_add_of_node - Add an of_node to an existing device
|
||||
* @dev: device whose device tree node is being added
|
||||
* @of_node: of_node to add
|
||||
*
|
||||
* Return: 0 on success or error code on failure.
|
||||
*/
|
||||
int device_add_of_node(struct device *dev, struct device_node *of_node)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!of_node)
|
||||
return -EINVAL;
|
||||
|
||||
dev = get_device(dev);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->of_node) {
|
||||
dev_err(dev, "Cannot replace node %pOF with %pOF\n",
|
||||
dev->of_node, of_node);
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dev->of_node = of_node_get(of_node);
|
||||
|
||||
if (!dev->fwnode)
|
||||
dev->fwnode = of_fwnode_handle(of_node);
|
||||
|
||||
ret = 0;
|
||||
end:
|
||||
put_device(dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_add_of_node);
|
||||
|
||||
/**
|
||||
* device_set_of_node_from_dev - reuse device-tree node of another device
|
||||
* @dev: device whose device-tree node is being set
|
||||
|
|
118
drivers/pci/of.c
118
drivers/pci/of.c
|
@ -653,8 +653,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);
|
||||
|
@ -711,11 +711,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:
|
||||
|
@ -724,7 +731,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
|
||||
|
|
|
@ -54,9 +54,13 @@ enum of_pci_prop_compatible {
|
|||
static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,
|
||||
u32 reg_num, u32 flags, bool reloc)
|
||||
{
|
||||
prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) |
|
||||
FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) |
|
||||
FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn));
|
||||
if (pdev) {
|
||||
prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) |
|
||||
FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) |
|
||||
FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn));
|
||||
} else
|
||||
prop[0] = 0;
|
||||
|
||||
prop[0] |= flags | reg_num;
|
||||
if (!reloc) {
|
||||
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,
|
|||
}
|
||||
}
|
||||
|
||||
static int of_pci_get_addr_flags(struct resource *res, u32 *flags)
|
||||
static int of_pci_get_addr_flags(const struct resource *res, u32 *flags)
|
||||
{
|
||||
u32 ss;
|
||||
|
||||
|
@ -390,3 +394,106 @@ int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool of_pci_is_range_resource(const struct resource *res, u32 *flags)
|
||||
{
|
||||
if (!(resource_type(res) & IORESOURCE_MEM) &&
|
||||
!(resource_type(res) & IORESOURCE_MEM_64))
|
||||
return false;
|
||||
|
||||
if (of_pci_get_addr_flags(res, flags))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int of_pci_host_bridge_prop_ranges(struct pci_host_bridge *bridge,
|
||||
struct of_changeset *ocs,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct resource_entry *window;
|
||||
unsigned int ranges_sz = 0;
|
||||
unsigned int n_range = 0;
|
||||
struct resource *res;
|
||||
int n_addr_cells;
|
||||
u32 *ranges;
|
||||
u64 val64;
|
||||
u32 flags;
|
||||
int ret;
|
||||
|
||||
n_addr_cells = of_n_addr_cells(np);
|
||||
if (n_addr_cells <= 0 || n_addr_cells > 2)
|
||||
return -EINVAL;
|
||||
|
||||
resource_list_for_each_entry(window, &bridge->windows) {
|
||||
res = window->res;
|
||||
if (!of_pci_is_range_resource(res, &flags))
|
||||
continue;
|
||||
n_range++;
|
||||
}
|
||||
|
||||
if (!n_range)
|
||||
return 0;
|
||||
|
||||
ranges = kcalloc(n_range,
|
||||
(OF_PCI_ADDRESS_CELLS + OF_PCI_SIZE_CELLS +
|
||||
n_addr_cells) * sizeof(*ranges),
|
||||
GFP_KERNEL);
|
||||
if (!ranges)
|
||||
return -ENOMEM;
|
||||
|
||||
resource_list_for_each_entry(window, &bridge->windows) {
|
||||
res = window->res;
|
||||
if (!of_pci_is_range_resource(res, &flags))
|
||||
continue;
|
||||
|
||||
/* PCI bus address */
|
||||
val64 = res->start;
|
||||
of_pci_set_address(NULL, &ranges[ranges_sz],
|
||||
val64 - window->offset, 0, flags, false);
|
||||
ranges_sz += OF_PCI_ADDRESS_CELLS;
|
||||
|
||||
/* Host bus address */
|
||||
if (n_addr_cells == 2)
|
||||
ranges[ranges_sz++] = upper_32_bits(val64);
|
||||
ranges[ranges_sz++] = lower_32_bits(val64);
|
||||
|
||||
/* Size */
|
||||
val64 = resource_size(res);
|
||||
ranges[ranges_sz] = upper_32_bits(val64);
|
||||
ranges[ranges_sz + 1] = lower_32_bits(val64);
|
||||
ranges_sz += OF_PCI_SIZE_CELLS;
|
||||
}
|
||||
|
||||
ret = of_changeset_add_prop_u32_array(ocs, np, "ranges", ranges,
|
||||
ranges_sz);
|
||||
kfree(ranges);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge,
|
||||
struct of_changeset *ocs,
|
||||
struct device_node *np)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = of_changeset_add_prop_string(ocs, np, "device_type", "pci");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_changeset_add_prop_u32(ocs, np, "#address-cells",
|
||||
OF_PCI_ADDRESS_CELLS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_changeset_add_prop_u32(ocs, np, "#size-cells",
|
||||
OF_PCI_SIZE_CELLS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_pci_host_bridge_prop_ranges(bridge, ocs, np);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -944,9 +944,16 @@ void of_pci_make_dev_node(struct pci_dev *pdev);
|
|||
void of_pci_remove_node(struct pci_dev *pdev);
|
||||
int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
|
||||
struct device_node *np);
|
||||
void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge);
|
||||
void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge);
|
||||
int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge,
|
||||
struct of_changeset *ocs,
|
||||
struct device_node *np);
|
||||
#else
|
||||
static inline void of_pci_make_dev_node(struct pci_dev *pdev) { }
|
||||
static inline void of_pci_remove_node(struct pci_dev *pdev) { }
|
||||
static inline void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) { }
|
||||
static inline void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIEAER
|
||||
|
|
|
@ -1098,6 +1098,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
|||
dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
|
||||
}
|
||||
|
||||
of_pci_make_host_bridge_node(bridge);
|
||||
|
||||
down_write(&pci_bus_sem);
|
||||
list_add_tail(&bus->node, &pci_root_buses);
|
||||
up_write(&pci_bus_sem);
|
||||
|
|
|
@ -164,6 +164,8 @@ void pci_stop_root_bus(struct pci_bus *bus)
|
|||
&bus->devices, bus_list)
|
||||
pci_stop_bus_device(child);
|
||||
|
||||
of_pci_remove_host_bridge_node(host_bridge);
|
||||
|
||||
/* stop the host bridge */
|
||||
device_release_driver(&host_bridge->dev);
|
||||
}
|
||||
|
|
|
@ -1191,6 +1191,8 @@ int device_online(struct device *dev);
|
|||
void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
|
||||
void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
|
||||
void device_set_node(struct device *dev, struct fwnode_handle *fwnode);
|
||||
int device_add_of_node(struct device *dev, struct device_node *of_node);
|
||||
void device_remove_of_node(struct device *dev);
|
||||
void device_set_of_node_from_dev(struct device *dev, const struct device *dev2);
|
||||
|
||||
static inline struct device_node *dev_of_node(struct device *dev)
|
||||
|
|
Loading…
Reference in New Issue
Block a user