2022-03-05 07:51:50

by Lizhi Hou

[permalink] [raw]
Subject: [PATCH V1 RESEND 1/4] pci: add interface to create pci-ep device tree node

This patch enables PCIe device to uses flattened device tree to describe
apertures in its PCIe BARs. The aperture address consists of PCIe BAR index
and offset.

For this kind of device, the driver probe routine calls the new added
interface to create a device tree node. This device tree node is attached
under system device tree root. Then the driver may load the flatten device
tree overlay and attach it under this node. And the node also contains
'ranges' property which is used to translate aperture address(BAR index
and offset) to CPU address.

Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
---
drivers/pci/of.c | 180 +++++++++++++++++++++++++++++++++++++++++
include/linux/of_pci.h | 15 ++++
2 files changed, 195 insertions(+)

diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index cb2e8351c2cc..198f08351070 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -605,6 +605,186 @@ int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge)
return pci_parse_request_of_pci_ranges(dev, bridge);
}

+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+
+static void devm_of_pci_destroy_bus_endpoint(struct device *dev, void *res)
+{
+ struct device_node *node = res;
+
+ of_detach_node(node);
+}
+
+static int of_ep_add_property(struct device *dev, struct property **proplist, const char *name,
+ const int length, void *value)
+{
+ struct property *new;
+
+ new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ new->name = devm_kstrdup(dev, name, GFP_KERNEL);
+ if (!new->name)
+ return -ENOMEM;
+
+ new->value = devm_kmalloc(dev, length, GFP_KERNEL);
+ if (!new->value)
+ return -ENOMEM;
+
+ memcpy(new->value, value, length);
+ new->length = length;
+ new->next = *proplist;
+ *proplist = new;
+
+ return 0;
+}
+
+static struct device_node *of_ep_alloc_node(struct pci_dev *pdev, const char *name)
+{
+ struct device_node *node;
+ char *full_name;
+
+ node = devres_alloc(devm_of_pci_destroy_bus_endpoint, sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return NULL;
+
+ full_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "/%s@%llx", name,
+ (u64)pci_resource_start(pdev, 0));
+ if (!full_name)
+ return NULL;
+
+ node->parent = of_root;
+ node->full_name = full_name;
+ of_node_set_flag(node, OF_DYNAMIC);
+ of_node_init(node);
+
+ return node;
+}
+
+/**
+ * devm_of_pci_create_bus_endpoint - Create a device node for the given pci device.
+ * @pdev: PCI device pointer.
+ *
+ * For PCI device which uses flattened device tree to describe apertures in its BARs,
+ * a device node for the given pci device is required. Then the flattened device tree
+ * overlay from the device can be applied to the base tree.
+ * The device node is under root node and act like bus node. It contains a "ranges"
+ * property which is used for address translation of its children. Each child node
+ * corresponds an aperture and use BAR index and offset as its address.
+
+ * Returns 0 on success or a negative error-code on failure.
+ */
+int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev)
+{
+ struct property *proplist = NULL;
+ struct device *dev = &pdev->dev;
+ int range_ncells, addr_ncells;
+ struct device_node *node;
+ void *prop = NULL;
+ u32 *range_cell;
+ __be32 val;
+ int i, ret;
+
+ node = of_ep_alloc_node(pdev, "pci-ep-bus");
+ if (!node)
+ return -ENOMEM;
+
+ /* the endpoint node works as 'simple-bus' to translate aperture addresses. */
+ prop = "simple-bus";
+ ret = of_ep_add_property(dev, &proplist, "compatible", strlen(prop) + 1, prop);
+ if (ret)
+ goto cleanup;
+
+ /* The address and size cells of nodes underneath are 2 */
+ val = cpu_to_be32(2);
+ ret = of_ep_add_property(dev, &proplist, "#address-cells", sizeof(u32), &val);
+ if (ret)
+ goto cleanup;
+
+ ret = of_ep_add_property(dev, &proplist, "#size-cells", sizeof(u32), &val);
+ if (ret)
+ goto cleanup;
+
+ /* child address format: 0xIooooooo oooooooo, I = bar index, o = offset on bar */
+ addr_ncells = of_n_addr_cells(node);
+ if (addr_ncells > 2) {
+ /* does not support number of address cells greater than 2 */
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ /* range cells include <node addr cells> <child addr cells> <child size cells> */
+ range_ncells = addr_ncells + 4;
+ prop = kzalloc(range_ncells * sizeof(u32) * PCI_STD_NUM_BARS, GFP_KERNEL);
+ if (!prop) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ range_cell = prop;
+ for (i = 0; i < PCI_STD_NUM_BARS; i++) {
+ if (!pci_resource_len(pdev, i))
+ continue;
+ /* highest 4 bits of address are bar index */
+ *(__be64 *)range_cell = cpu_to_be64((u64)i << 60);
+ range_cell += 2;
+ if (addr_ncells == 2)
+ *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_start(pdev, i));
+ else
+ *(__be32 *)range_cell = cpu_to_be32((u32)pci_resource_start(pdev, i));
+
+ range_cell += addr_ncells;
+ *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_len(pdev, i));
+ range_cell += 2;
+ }
+
+ /* error out if there is not PCI BAR been found */
+ if ((void *)range_cell == prop) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ ret = of_ep_add_property(dev, &proplist, "ranges", (void *)range_cell - prop, prop);
+ kfree(prop);
+ if (ret)
+ goto cleanup;
+
+ node->properties = proplist;
+ ret = of_attach_node(node);
+ if (ret)
+ goto cleanup;
+
+ devres_add(dev, node);
+
+ return 0;
+
+cleanup:
+ kfree(prop);
+ if (node)
+ devres_free(node);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_of_pci_create_bus_endpoint);
+
+struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev)
+{
+ struct device_node *dn;
+ char *path;
+
+ path = kasprintf(GFP_KERNEL, "/pci-ep-bus@%llx",
+ (u64)pci_resource_start(pdev, 0));
+ if (!path)
+ return NULL;
+
+ dn = of_find_node_by_path(path);
+ kfree(path);
+
+ return dn;
+}
+EXPORT_SYMBOL_GPL(of_pci_find_bus_endpoint);
+#endif /* CONFIG_OF_DYNAMIC */
+
#endif /* CONFIG_PCI */

/**
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index 29658c0ee71f..c1d86be321b2 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -38,4 +38,19 @@ of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
}
#endif

+#if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_PCI)
+int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev);
+struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev);
+#else
+static inline int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev)
+{
+ return -EINVAL;
+}
+
+static inline struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev)
+{
+ return NULL;
+}
+#endif
+
#endif
--
2.27.0


2022-03-10 12:05:18

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH V1 RESEND 1/4] pci: add interface to create pci-ep device tree node

Hi Lizhi,

url: https://github.com/0day-ci/linux/commits/Lizhi-Hou/Infrastructure-to-define-apertures-in-a-PCIe-device-with-a-flattened-device-tree/20220307-141939
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: i386-randconfig-m021-20220307 (https://download.01.org/0day-ci/archive/20220310/[email protected]/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
Reported-by: Dan Carpenter <[email protected]>

smatch warnings:
drivers/pci/of.c:762 devm_of_pci_create_bus_endpoint() error: double free of 'prop'

vim +/prop +762 drivers/pci/of.c

3a2c08c0f0ef77 Lizhi Hou 2022-03-04 677 int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 678 {
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 679 struct property *proplist = NULL;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 680 struct device *dev = &pdev->dev;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 681 int range_ncells, addr_ncells;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 682 struct device_node *node;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 683 void *prop = NULL;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 684 u32 *range_cell;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 685 __be32 val;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 686 int i, ret;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 687
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 688 node = of_ep_alloc_node(pdev, "pci-ep-bus");
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 689 if (!node)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 690 return -ENOMEM;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 691
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 692 /* the endpoint node works as 'simple-bus' to translate aperture addresses. */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 693 prop = "simple-bus";
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 694 ret = of_ep_add_property(dev, &proplist, "compatible", strlen(prop) + 1, prop);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 695 if (ret)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 696 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 697
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 698 /* The address and size cells of nodes underneath are 2 */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 699 val = cpu_to_be32(2);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 700 ret = of_ep_add_property(dev, &proplist, "#address-cells", sizeof(u32), &val);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 701 if (ret)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 702 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 703
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 704 ret = of_ep_add_property(dev, &proplist, "#size-cells", sizeof(u32), &val);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 705 if (ret)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 706 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 707
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 708 /* child address format: 0xIooooooo oooooooo, I = bar index, o = offset on bar */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 709 addr_ncells = of_n_addr_cells(node);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 710 if (addr_ncells > 2) {
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 711 /* does not support number of address cells greater than 2 */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 712 ret = -EINVAL;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 713 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 714 }
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 715
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 716 /* range cells include <node addr cells> <child addr cells> <child size cells> */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 717 range_ncells = addr_ncells + 4;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 718 prop = kzalloc(range_ncells * sizeof(u32) * PCI_STD_NUM_BARS, GFP_KERNEL);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 719 if (!prop) {
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 720 ret = -ENOMEM;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 721 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 722 }
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 723
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 724 range_cell = prop;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 725 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 726 if (!pci_resource_len(pdev, i))
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 727 continue;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 728 /* highest 4 bits of address are bar index */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 729 *(__be64 *)range_cell = cpu_to_be64((u64)i << 60);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 730 range_cell += 2;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 731 if (addr_ncells == 2)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 732 *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_start(pdev, i));
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 733 else
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 734 *(__be32 *)range_cell = cpu_to_be32((u32)pci_resource_start(pdev, i));
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 735
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 736 range_cell += addr_ncells;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 737 *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_len(pdev, i));
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 738 range_cell += 2;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 739 }
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 740
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 741 /* error out if there is not PCI BAR been found */
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 742 if ((void *)range_cell == prop) {
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 743 ret = -EINVAL;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 744 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 745 }
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 746
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 747 ret = of_ep_add_property(dev, &proplist, "ranges", (void *)range_cell - prop, prop);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 748 kfree(prop);
^^^^^^^^^^^^
Free

3a2c08c0f0ef77 Lizhi Hou 2022-03-04 749 if (ret)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 750 goto cleanup;
^^^^^^^^^^^^^
Double free after goto.


3a2c08c0f0ef77 Lizhi Hou 2022-03-04 751
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 752 node->properties = proplist;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 753 ret = of_attach_node(node);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 754 if (ret)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 755 goto cleanup;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 756
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 757 devres_add(dev, node);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 758
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 759 return 0;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 760
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 761 cleanup:
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 @762 kfree(prop);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 763 if (node)
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 764 devres_free(node);
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 765
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 766 return ret;
3a2c08c0f0ef77 Lizhi Hou 2022-03-04 767 }

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]

2022-03-10 22:12:20

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH V1 RESEND 1/4] pci: add interface to create pci-ep device tree node

Run "git log --oneline drivers/pci/of.c" and follow the convention,
e.g., something like:

PCI: Add DT Endpoint description interfaces

On Fri, Mar 04, 2022 at 09:23:01PM -0800, Lizhi Hou wrote:
> This patch enables PCIe device to uses flattened device tree to describe
> apertures in its PCIe BARs. The aperture address consists of PCIe BAR index
> and offset.
>
> For this kind of device, the driver probe routine calls the new added
> interface to create a device tree node. This device tree node is attached
> under system device tree root. Then the driver may load the flatten device
> tree overlay and attach it under this node. And the node also contains
> 'ranges' property which is used to translate aperture address(BAR index
> and offset) to CPU address.

In the commit log, please say *what* this patch does and why we need
it. The current text talks about how some interface might be used,
but doesn't specifically say what interface that is or that this patch
adds it.

It should also have a specific pointer to the relevant DT binding.

> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> ---
> drivers/pci/of.c | 180 +++++++++++++++++++++++++++++++++++++++++
> include/linux/of_pci.h | 15 ++++
> 2 files changed, 195 insertions(+)
>
> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
> index cb2e8351c2cc..198f08351070 100644
> --- a/drivers/pci/of.c
> +++ b/drivers/pci/of.c
> @@ -605,6 +605,186 @@ int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge)
> return pci_parse_request_of_pci_ranges(dev, bridge);
> }
>
> +#if IS_ENABLED(CONFIG_OF_DYNAMIC)
> +
> +static void devm_of_pci_destroy_bus_endpoint(struct device *dev, void *res)
> +{
> + struct device_node *node = res;
> +
> + of_detach_node(node);
> +}
> +
> +static int of_ep_add_property(struct device *dev, struct property **proplist, const char *name,

Please rewrap code and comments to fit in 80 columns like the rest of
the file. There's a lot more below that I snipped out.

Bjorn

2022-06-21 15:48:36

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH V1 RESEND 1/4] pci: add interface to create pci-ep device tree node

On Fri, Mar 04, 2022 at 09:23:01PM -0800, Lizhi Hou wrote:
> This patch enables PCIe device to uses flattened device tree to describe
> apertures in its PCIe BARs. The aperture address consists of PCIe BAR index
> and offset.
>
> For this kind of device, the driver probe routine calls the new added
> interface to create a device tree node. This device tree node is attached
> under system device tree root. Then the driver may load the flatten device
> tree overlay and attach it under this node. And the node also contains
> 'ranges' property which is used to translate aperture address(BAR index
> and offset) to CPU address.
>

This is the devicetree support for the PCI endpoint subsystem. Hence, the
code should live under drivers/pci/endpoint/.

But let's first settle on the structure of the devicetree binding first.

Thanks,
Mani

> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> ---
> drivers/pci/of.c | 180 +++++++++++++++++++++++++++++++++++++++++
> include/linux/of_pci.h | 15 ++++
> 2 files changed, 195 insertions(+)
>
> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
> index cb2e8351c2cc..198f08351070 100644
> --- a/drivers/pci/of.c
> +++ b/drivers/pci/of.c
> @@ -605,6 +605,186 @@ int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge)
> return pci_parse_request_of_pci_ranges(dev, bridge);
> }
>
> +#if IS_ENABLED(CONFIG_OF_DYNAMIC)
> +
> +static void devm_of_pci_destroy_bus_endpoint(struct device *dev, void *res)
> +{
> + struct device_node *node = res;
> +
> + of_detach_node(node);
> +}
> +
> +static int of_ep_add_property(struct device *dev, struct property **proplist, const char *name,
> + const int length, void *value)
> +{
> + struct property *new;
> +
> + new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL);
> + if (!new)
> + return -ENOMEM;
> +
> + new->name = devm_kstrdup(dev, name, GFP_KERNEL);
> + if (!new->name)
> + return -ENOMEM;
> +
> + new->value = devm_kmalloc(dev, length, GFP_KERNEL);
> + if (!new->value)
> + return -ENOMEM;
> +
> + memcpy(new->value, value, length);
> + new->length = length;
> + new->next = *proplist;
> + *proplist = new;
> +
> + return 0;
> +}
> +
> +static struct device_node *of_ep_alloc_node(struct pci_dev *pdev, const char *name)
> +{
> + struct device_node *node;
> + char *full_name;
> +
> + node = devres_alloc(devm_of_pci_destroy_bus_endpoint, sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return NULL;
> +
> + full_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "/%s@%llx", name,
> + (u64)pci_resource_start(pdev, 0));
> + if (!full_name)
> + return NULL;
> +
> + node->parent = of_root;
> + node->full_name = full_name;
> + of_node_set_flag(node, OF_DYNAMIC);
> + of_node_init(node);
> +
> + return node;
> +}
> +
> +/**
> + * devm_of_pci_create_bus_endpoint - Create a device node for the given pci device.
> + * @pdev: PCI device pointer.
> + *
> + * For PCI device which uses flattened device tree to describe apertures in its BARs,
> + * a device node for the given pci device is required. Then the flattened device tree
> + * overlay from the device can be applied to the base tree.
> + * The device node is under root node and act like bus node. It contains a "ranges"
> + * property which is used for address translation of its children. Each child node
> + * corresponds an aperture and use BAR index and offset as its address.
> +
> + * Returns 0 on success or a negative error-code on failure.
> + */
> +int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev)
> +{
> + struct property *proplist = NULL;
> + struct device *dev = &pdev->dev;
> + int range_ncells, addr_ncells;
> + struct device_node *node;
> + void *prop = NULL;
> + u32 *range_cell;
> + __be32 val;
> + int i, ret;
> +
> + node = of_ep_alloc_node(pdev, "pci-ep-bus");
> + if (!node)
> + return -ENOMEM;
> +
> + /* the endpoint node works as 'simple-bus' to translate aperture addresses. */
> + prop = "simple-bus";
> + ret = of_ep_add_property(dev, &proplist, "compatible", strlen(prop) + 1, prop);
> + if (ret)
> + goto cleanup;
> +
> + /* The address and size cells of nodes underneath are 2 */
> + val = cpu_to_be32(2);
> + ret = of_ep_add_property(dev, &proplist, "#address-cells", sizeof(u32), &val);
> + if (ret)
> + goto cleanup;
> +
> + ret = of_ep_add_property(dev, &proplist, "#size-cells", sizeof(u32), &val);
> + if (ret)
> + goto cleanup;
> +
> + /* child address format: 0xIooooooo oooooooo, I = bar index, o = offset on bar */
> + addr_ncells = of_n_addr_cells(node);
> + if (addr_ncells > 2) {
> + /* does not support number of address cells greater than 2 */
> + ret = -EINVAL;
> + goto cleanup;
> + }
> +
> + /* range cells include <node addr cells> <child addr cells> <child size cells> */
> + range_ncells = addr_ncells + 4;
> + prop = kzalloc(range_ncells * sizeof(u32) * PCI_STD_NUM_BARS, GFP_KERNEL);
> + if (!prop) {
> + ret = -ENOMEM;
> + goto cleanup;
> + }
> +
> + range_cell = prop;
> + for (i = 0; i < PCI_STD_NUM_BARS; i++) {
> + if (!pci_resource_len(pdev, i))
> + continue;
> + /* highest 4 bits of address are bar index */
> + *(__be64 *)range_cell = cpu_to_be64((u64)i << 60);
> + range_cell += 2;
> + if (addr_ncells == 2)
> + *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_start(pdev, i));
> + else
> + *(__be32 *)range_cell = cpu_to_be32((u32)pci_resource_start(pdev, i));
> +
> + range_cell += addr_ncells;
> + *(__be64 *)range_cell = cpu_to_be64((u64)pci_resource_len(pdev, i));
> + range_cell += 2;
> + }
> +
> + /* error out if there is not PCI BAR been found */
> + if ((void *)range_cell == prop) {
> + ret = -EINVAL;
> + goto cleanup;
> + }
> +
> + ret = of_ep_add_property(dev, &proplist, "ranges", (void *)range_cell - prop, prop);
> + kfree(prop);
> + if (ret)
> + goto cleanup;
> +
> + node->properties = proplist;
> + ret = of_attach_node(node);
> + if (ret)
> + goto cleanup;
> +
> + devres_add(dev, node);
> +
> + return 0;
> +
> +cleanup:
> + kfree(prop);
> + if (node)
> + devres_free(node);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(devm_of_pci_create_bus_endpoint);
> +
> +struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev)
> +{
> + struct device_node *dn;
> + char *path;
> +
> + path = kasprintf(GFP_KERNEL, "/pci-ep-bus@%llx",
> + (u64)pci_resource_start(pdev, 0));
> + if (!path)
> + return NULL;
> +
> + dn = of_find_node_by_path(path);
> + kfree(path);
> +
> + return dn;
> +}
> +EXPORT_SYMBOL_GPL(of_pci_find_bus_endpoint);
> +#endif /* CONFIG_OF_DYNAMIC */
> +
> #endif /* CONFIG_PCI */
>
> /**
> diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
> index 29658c0ee71f..c1d86be321b2 100644
> --- a/include/linux/of_pci.h
> +++ b/include/linux/of_pci.h
> @@ -38,4 +38,19 @@ of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
> }
> #endif
>
> +#if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_PCI)
> +int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev);
> +struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev);
> +#else
> +static inline int devm_of_pci_create_bus_endpoint(struct pci_dev *pdev)
> +{
> + return -EINVAL;
> +}
> +
> +static inline struct device_node *of_pci_find_bus_endpoint(struct pci_dev *pdev)
> +{
> + return NULL;
> +}
> +#endif
> +
> #endif
> --
> 2.27.0
>

--
மணிவண்ணன் சதாசிவம்