|
|
|
@@ -22,6 +22,7 @@
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include <linux/device.h>
|
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
|
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
@@ -3326,6 +3327,7 @@ OVERLAY_INFO_EXTERN(overlay_gpio_02b);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_gpio_03);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_gpio_04a);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_gpio_04b);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_pci_node);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop);
|
|
|
|
|
OVERLAY_INFO_EXTERN(overlay_bad_phandle);
|
|
|
|
@@ -3361,6 +3363,7 @@ static struct overlay_info overlays[] = {
|
|
|
|
|
OVERLAY_INFO(overlay_gpio_03, 0),
|
|
|
|
|
OVERLAY_INFO(overlay_gpio_04a, 0),
|
|
|
|
|
OVERLAY_INFO(overlay_gpio_04b, 0),
|
|
|
|
|
OVERLAY_INFO(overlay_pci_node, 0),
|
|
|
|
|
OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL),
|
|
|
|
|
OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL),
|
|
|
|
|
OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
|
|
|
|
@@ -3731,6 +3734,191 @@ static inline __init void of_unittest_overlay_high_level(void) {}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_PCI_DYNAMIC_OF_NODES
|
|
|
|
|
|
|
|
|
|
static int of_unittest_pci_dev_num;
|
|
|
|
|
static int of_unittest_pci_child_num;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* PCI device tree node test driver
|
|
|
|
|
*/
|
|
|
|
|
static const struct pci_device_id testdrv_pci_ids[] = {
|
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x5), }, /* PCI_VENDOR_ID_REDHAT */
|
|
|
|
|
{ 0, }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int testdrv_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
|
|
{
|
|
|
|
|
struct overlay_info *info;
|
|
|
|
|
struct device_node *dn;
|
|
|
|
|
int ret, ovcs_id;
|
|
|
|
|
u32 size;
|
|
|
|
|
|
|
|
|
|
dn = pdev->dev.of_node;
|
|
|
|
|
if (!dn) {
|
|
|
|
|
dev_err(&pdev->dev, "does not find bus endpoint");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (info = overlays; info && info->name; info++) {
|
|
|
|
|
if (!strcmp(info->name, "overlay_pci_node"))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!info || !info->name) {
|
|
|
|
|
dev_err(&pdev->dev, "no overlay data for overlay_pci_node");
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = info->dtbo_end - info->dtbo_begin;
|
|
|
|
|
ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn);
|
|
|
|
|
of_node_put(dn);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
of_platform_default_populate(dn, NULL, &pdev->dev);
|
|
|
|
|
pci_set_drvdata(pdev, (void *)(uintptr_t)ovcs_id);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void testdrv_remove(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
|
int ovcs_id = (int)(uintptr_t)pci_get_drvdata(pdev);
|
|
|
|
|
|
|
|
|
|
of_platform_depopulate(&pdev->dev);
|
|
|
|
|
of_overlay_remove(&ovcs_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct pci_driver testdrv_driver = {
|
|
|
|
|
.name = "pci_dt_testdrv",
|
|
|
|
|
.id_table = testdrv_pci_ids,
|
|
|
|
|
.probe = testdrv_probe,
|
|
|
|
|
.remove = testdrv_remove,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int unittest_pci_probe(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
struct resource *res;
|
|
|
|
|
struct device *dev;
|
|
|
|
|
u64 exp_addr;
|
|
|
|
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
|
if (!res)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
dev = &pdev->dev;
|
|
|
|
|
while (dev && !dev_is_pci(dev))
|
|
|
|
|
dev = dev->parent;
|
|
|
|
|
if (!dev) {
|
|
|
|
|
pr_err("unable to find parent device\n");
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exp_addr = pci_resource_start(to_pci_dev(dev), 0) + 0x100;
|
|
|
|
|
unittest(res->start == exp_addr, "Incorrect translated address %llx, expected %llx\n",
|
|
|
|
|
(u64)res->start, exp_addr);
|
|
|
|
|
|
|
|
|
|
of_unittest_pci_child_num++;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct of_device_id unittest_pci_of_match[] = {
|
|
|
|
|
{ .compatible = "unittest-pci" },
|
|
|
|
|
{ }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct platform_driver unittest_pci_driver = {
|
|
|
|
|
.probe = unittest_pci_probe,
|
|
|
|
|
.driver = {
|
|
|
|
|
.name = "unittest-pci",
|
|
|
|
|
.of_match_table = unittest_pci_of_match,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int of_unittest_pci_node_verify(struct pci_dev *pdev, bool add)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *pnp, *np = NULL;
|
|
|
|
|
struct device *child_dev;
|
|
|
|
|
char *path = NULL;
|
|
|
|
|
const __be32 *reg;
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
pnp = pdev->dev.of_node;
|
|
|
|
|
unittest(pnp, "Failed creating PCI dt node\n");
|
|
|
|
|
if (!pnp)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
if (add) {
|
|
|
|
|
path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0/unittest-pci@100", pnp);
|
|
|
|
|
np = of_find_node_by_path(path);
|
|
|
|
|
unittest(np, "Failed to get unittest-pci node under PCI node\n");
|
|
|
|
|
if (!np) {
|
|
|
|
|
rc = -ENODEV;
|
|
|
|
|
goto failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reg = of_get_property(np, "reg", NULL);
|
|
|
|
|
unittest(reg, "Failed to get reg property\n");
|
|
|
|
|
if (!reg)
|
|
|
|
|
rc = -ENODEV;
|
|
|
|
|
} else {
|
|
|
|
|
path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0", pnp);
|
|
|
|
|
np = of_find_node_by_path(path);
|
|
|
|
|
unittest(!np, "Child device tree node is not removed\n");
|
|
|
|
|
child_dev = device_find_any_child(&pdev->dev);
|
|
|
|
|
unittest(!child_dev, "Child device is not removed\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
failed:
|
|
|
|
|
kfree(path);
|
|
|
|
|
if (np)
|
|
|
|
|
of_node_put(np);
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __init of_unittest_pci_node(void)
|
|
|
|
|
{
|
|
|
|
|
struct pci_dev *pdev = NULL;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
rc = pci_register_driver(&testdrv_driver);
|
|
|
|
|
unittest(!rc, "Failed to register pci test driver; rc = %d\n", rc);
|
|
|
|
|
if (rc)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rc = platform_driver_register(&unittest_pci_driver);
|
|
|
|
|
if (unittest(!rc, "Failed to register unittest pci driver\n")) {
|
|
|
|
|
pci_unregister_driver(&testdrv_driver);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL) {
|
|
|
|
|
of_unittest_pci_node_verify(pdev, true);
|
|
|
|
|
of_unittest_pci_dev_num++;
|
|
|
|
|
}
|
|
|
|
|
if (pdev)
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
|
|
|
|
|
unittest(of_unittest_pci_dev_num,
|
|
|
|
|
"No test PCI device been found. Please run QEMU with '-device pci-testdev'\n");
|
|
|
|
|
unittest(of_unittest_pci_dev_num == of_unittest_pci_child_num,
|
|
|
|
|
"Child device number %d is not expected %d", of_unittest_pci_child_num,
|
|
|
|
|
of_unittest_pci_dev_num);
|
|
|
|
|
|
|
|
|
|
platform_driver_unregister(&unittest_pci_driver);
|
|
|
|
|
pci_unregister_driver(&testdrv_driver);
|
|
|
|
|
|
|
|
|
|
while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL)
|
|
|
|
|
of_unittest_pci_node_verify(pdev, false);
|
|
|
|
|
if (pdev)
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static void __init of_unittest_pci_node(void) { }
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int __init of_unittest(void)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *np;
|
|
|
|
@@ -3781,6 +3969,7 @@ static int __init of_unittest(void)
|
|
|
|
|
of_unittest_platform_populate();
|
|
|
|
|
of_unittest_overlay();
|
|
|
|
|
of_unittest_lifecycle();
|
|
|
|
|
of_unittest_pci_node();
|
|
|
|
|
|
|
|
|
|
/* Double check linkage after removing testcase data */
|
|
|
|
|
of_unittest_check_tree_linkage();
|
|
|
|
|