On 2023-04-20 20:38, Xu Yilun wrote: > On 2023-04-17 at 14:23:07 +0200, Marco Pagani wrote: >> Add fake FPGA region platform driver with support functions. This >> module is part of the KUnit tests for the FPGA subsystem. >> >> Signed-off-by: Marco Pagani <marpagan@xxxxxxxxxx> >> --- >> drivers/fpga/tests/fake-fpga-region.c | 259 ++++++++++++++++++++++++++ >> drivers/fpga/tests/fake-fpga-region.h | 40 ++++ >> 2 files changed, 299 insertions(+) >> create mode 100644 drivers/fpga/tests/fake-fpga-region.c >> create mode 100644 drivers/fpga/tests/fake-fpga-region.h >> >> diff --git a/drivers/fpga/tests/fake-fpga-region.c b/drivers/fpga/tests/fake-fpga-region.c >> new file mode 100644 >> index 000000000000..b23ae5e94fe6 >> --- /dev/null >> +++ b/drivers/fpga/tests/fake-fpga-region.c >> @@ -0,0 +1,259 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Driver for the fake FPGA region >> + * >> + * Copyright (C) 2023 Red Hat, Inc. >> + * >> + * Author: Marco Pagani <marpagan@xxxxxxxxxx> >> + */ >> + >> +#include <linux/device.h> >> +#include <linux/list.h> >> +#include <linux/platform_device.h> >> +#include <linux/fpga/fpga-mgr.h> >> +#include <linux/fpga/fpga-region.h> >> +#include <linux/fpga/fpga-bridge.h> >> +#include <kunit/test.h> >> + >> +#include "fake-fpga-region.h" >> + >> +#define FAKE_FPGA_REGION_DEV_NAME "fake_fpga_region" >> + >> +struct fake_region_priv { >> + int id; >> + struct kunit *test; >> + struct list_head bridge_list; >> +}; >> + >> +struct bridge_elem { >> + struct fpga_bridge *bridge; >> + struct list_head node; >> +}; >> + >> +struct fake_region_data { >> + struct fpga_manager *mgr; >> + struct kunit *test; >> +}; >> + >> +/** >> + * fake_fpga_region_register() - register a fake FPGA region. >> + * @mgr: associated FPGA manager. >> + * @parent: parent device. >> + * @test: KUnit test context object. >> + * >> + * Return: pointer to a new fake FPGA region on success, an ERR_PTR() encoded >> + * error code on failure. >> + */ >> +struct fake_fpga_region * >> +fake_fpga_region_register(struct fpga_manager *mgr, struct device *parent, >> + struct kunit *test) >> +{ >> + struct fake_fpga_region *region_ctx; >> + struct fake_region_data pdata; >> + struct fake_region_priv *priv; >> + int ret; >> + >> + region_ctx = kzalloc(sizeof(*region_ctx), GFP_KERNEL); >> + if (!region_ctx) { >> + ret = -ENOMEM; >> + goto err_mem; >> + } >> + >> + region_ctx->pdev = platform_device_alloc(FAKE_FPGA_REGION_DEV_NAME, >> + PLATFORM_DEVID_AUTO); >> + if (!region_ctx->pdev) { >> + pr_err("Fake FPGA region device allocation failed\n"); >> + ret = -ENOMEM; >> + goto err_mem; >> + } >> + >> + pdata.mgr = mgr; >> + pdata.test = test; >> + platform_device_add_data(region_ctx->pdev, &pdata, sizeof(pdata)); >> + >> + region_ctx->pdev->dev.parent = parent; >> + ret = platform_device_add(region_ctx->pdev); >> + if (ret) { >> + pr_err("Fake FPGA region device add failed\n"); >> + goto err_pdev; >> + } >> + >> + region_ctx->region = platform_get_drvdata(region_ctx->pdev); >> + >> + if (test) { >> + priv = region_ctx->region->priv; >> + kunit_info(test, "Fake FPGA region %d registered\n", priv->id); >> + } >> + >> + return region_ctx; >> + >> +err_pdev: >> + platform_device_put(region_ctx->pdev); >> + kfree(region_ctx); >> +err_mem: >> + return ERR_PTR(ret); >> +} >> +EXPORT_SYMBOL_GPL(fake_fpga_region_register); >> + >> +/** >> + * fake_fpga_region_unregister() - unregister a fake FPGA region. >> + * @region_ctx: fake FPGA region context data structure. >> + */ >> +void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx) >> +{ >> + struct fake_region_priv *priv; >> + struct kunit *test; >> + int id; >> + >> + if (!region_ctx) >> + return; >> + >> + priv = region_ctx->region->priv; >> + test = priv->test; >> + id = priv->id; >> + >> + if (region_ctx->pdev) { >> + platform_device_unregister(region_ctx->pdev); >> + if (test) >> + kunit_info(test, "Fake FPGA region %d unregistered\n", id); >> + } >> + >> + kfree(region_ctx); >> +} >> +EXPORT_SYMBOL_GPL(fake_fpga_region_unregister); >> + >> +/** >> + * fake_fpga_region_add_bridge() - add a bridge to a fake FPGA region. >> + * @region_ctx: fake FPGA region context data structure. >> + * @bridge: FPGA bridge. >> + * >> + * Return: 0 if registration succeeded, an error code otherwise. >> + */ >> +int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx, >> + struct fpga_bridge *bridge) >> +{ >> + struct fake_region_priv *priv; >> + struct bridge_elem *elem; >> + >> + priv = region_ctx->region->priv; >> + >> + elem = devm_kzalloc(®ion_ctx->pdev->dev, sizeof(*elem), GFP_KERNEL); >> + if (!elem) >> + return -ENOMEM; >> + >> + /* Add bridge to the list of bridges in the private context */ >> + elem->bridge = bridge; >> + list_add(&elem->node, &priv->bridge_list); >> + >> + if (priv->test) >> + kunit_info(priv->test, "Bridge added to fake FPGA region %d\n", >> + priv->id); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(fake_fpga_region_add_bridge); > > To move these exported functions out of fake driver, could we also provide > the bridge list in platform_data? > > Thanks, > Yilun I feel that the function for adding bridges to the fake region should belong to the region driver itself rather than the fpga-test module. However, I can certainly include the bridge list also in platform_data. Thanks, Marco > >> + >> +int fake_fpga_region_program(struct fake_fpga_region *region_ctx) >> +{ >> + int ret; >> + >> + ret = fpga_region_program_fpga(region_ctx->region); >> + >> + /* fpga_region_program_fpga() already puts the bridges in case of errors */ >> + if (!ret) >> + fpga_bridges_put(®ion_ctx->region->bridge_list); >> + >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(fake_fpga_region_program); >> + >> +static int fake_region_get_bridges(struct fpga_region *region) >> +{ >> + struct fake_region_priv *priv; >> + struct bridge_elem *elem; >> + int ret; >> + >> + priv = region->priv; >> + >> + /* Copy the list of bridges from the private context to the region */ >> + list_for_each_entry(elem, &priv->bridge_list, node) { >> + ret = fpga_bridge_get_to_list(elem->bridge->dev.parent, >> + region->info, >> + ®ion->bridge_list); >> + if (ret) >> + break; >> + } >> + >> + return ret; >> +} >> + >> +static int fake_fpga_region_probe(struct platform_device *pdev) >> +{ >> + struct device *dev; >> + struct fpga_region *region; >> + struct fpga_manager *mgr; >> + struct fake_region_data *pdata; >> + struct fake_region_priv *priv; >> + struct fpga_region_info info; >> + static int id_count; >> + >> + dev = &pdev->dev; >> + pdata = dev_get_platdata(dev); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "Missing platform data\n"); >> + return -EINVAL; >> + } >> + >> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >> + if (!priv) >> + return -ENOMEM; >> + >> + mgr = fpga_mgr_get(pdata->mgr->dev.parent); >> + if (IS_ERR(mgr)) >> + return PTR_ERR(mgr); >> + >> + INIT_LIST_HEAD(&priv->bridge_list); >> + priv->id = id_count++; >> + priv->test = pdata->test; >> + >> + memset(&info, 0, sizeof(info)); >> + info.priv = priv; >> + info.mgr = mgr; >> + info.get_bridges = fake_region_get_bridges; >> + >> + region = fpga_region_register_full(dev, &info); >> + if (IS_ERR(region)) { >> + fpga_mgr_put(mgr); >> + return PTR_ERR(region); >> + } >> + >> + platform_set_drvdata(pdev, region); >> + >> + return 0; >> +} >> + >> +static int fake_fpga_region_remove(struct platform_device *pdev) >> +{ >> + struct fpga_region *region = platform_get_drvdata(pdev); >> + struct fpga_manager *mgr = region->mgr; >> + >> + fpga_mgr_put(mgr); >> + fpga_region_unregister(region); >> + >> + return 0; >> +} >> + >> +static struct platform_driver fake_fpga_region_drv = { >> + .driver = { >> + .name = FAKE_FPGA_REGION_DEV_NAME >> + }, >> + .probe = fake_fpga_region_probe, >> + .remove = fake_fpga_region_remove, >> +}; >> + >> +module_platform_driver(fake_fpga_region_drv); >> + >> +MODULE_AUTHOR("Marco Pagani <marpagan@xxxxxxxxxx>"); >> +MODULE_DESCRIPTION("Fake FPGA Bridge"); >> +MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/fpga/tests/fake-fpga-region.h b/drivers/fpga/tests/fake-fpga-region.h >> new file mode 100644 >> index 000000000000..976982c192bc >> --- /dev/null >> +++ b/drivers/fpga/tests/fake-fpga-region.h >> @@ -0,0 +1,40 @@ >> +/* SPDX-License-Identifier: GPL-2.0 */ >> +/* >> + * Header file for the fake FPGA region >> + * >> + * Copyright (C) 2023 Red Hat, Inc. >> + * >> + * Author: Marco Pagani <marpagan@xxxxxxxxxx> >> + */ >> + >> +#ifndef __FPGA_FAKE_RGN_H >> +#define __FPGA_FAKE_RGN_H >> + >> +#include <linux/platform_device.h> >> +#include <kunit/test.h> >> +#include <linux/fpga/fpga-mgr.h> >> +#include <linux/fpga/fpga-bridge.h> >> + >> +/** >> + * struct fake_fpga_region - fake FPGA region context data structure >> + * >> + * @region: FPGA region. >> + * @pdev: platform device of the FPGA region. >> + */ >> +struct fake_fpga_region { >> + struct fpga_region *region; >> + struct platform_device *pdev; >> +}; >> + >> +struct fake_fpga_region * >> +fake_fpga_region_register(struct fpga_manager *mgr, struct device *parent, >> + struct kunit *test); >> + >> +int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx, >> + struct fpga_bridge *bridge); >> + >> +int fake_fpga_region_program(struct fake_fpga_region *region_ctx); >> + >> +void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx); >> + >> +#endif /* __FPGA_FAKE_RGN_H */ >> -- >> 2.39.2 >> >