Re: [RFC PATCH 2/4] fpga: add fake FPGA region
From: Marco Pagani
Date: Wed Mar 01 2023 - 05:52:35 EST
On 2023-02-24 08:20, Xu Yilun wrote:
> On 2023-02-21 at 15:53:20 +0100, Marco Pagani wrote:
>>
>>
>> On 2023-02-18 11:13, Xu Yilun wrote:
>>> On 2023-02-03 at 18:06:51 +0100, Marco Pagani wrote:
>>>> Add fake FPGA region platform driver with support functions. This
>>>> module is part of the KUnit test suite for the FPGA subsystem.
>>>>
>>>> Signed-off-by: Marco Pagani <marpagan@xxxxxxxxxx>
>>>> ---
>>>> drivers/fpga/tests/fake-fpga-region.c | 186 ++++++++++++++++++++++++++
>>>> drivers/fpga/tests/fake-fpga-region.h | 37 +++++
>>>> 2 files changed, 223 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..095397e41837
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/tests/fake-fpga-region.c
>>>> @@ -0,0 +1,186 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Driver for fake FPGA region
>>>> + *
>>>> + * Copyright (C) 2023 Red Hat, Inc. All rights reserved.
>>>> + *
>>>> + * Author: Marco Pagani <marpagan@xxxxxxxxxx>
>>>> + */
>>>> +
>>>> +#include <linux/device.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 fake_region_data {
>>>> + struct fpga_manager *mgr;
>>>> + struct kunit *test;
>>>> +};
>>>> +
>>>> +/**
>>>> + * fake_fpga_region_register - register a fake FPGA region
>>>> + * @region_ctx: fake FPGA region context data structure.
>>>> + * @test: KUnit test context object.
>>>> + *
>>>> + * Return: 0 if registration succeeded, an error code otherwise.
>>>> + */
>>>> +int fake_fpga_region_register(struct fake_fpga_region *region_ctx,
>>>> + struct fpga_manager *mgr, struct kunit *test)
>>>> +{
>>>> + struct fake_region_data pdata;
>>>> + struct fake_region_priv *priv;
>>>> + int ret;
>>>> +
>>>> + pdata.mgr = mgr;
>>>> + pdata.test = test;
>>>> +
>>>> + region_ctx->pdev = platform_device_alloc(FAKE_FPGA_REGION_DEV_NAME,
>>>> + PLATFORM_DEVID_AUTO);
>>>> + if (IS_ERR(region_ctx->pdev)) {
>>>> + pr_err("Fake FPGA region device allocation failed\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + platform_device_add_data(region_ctx->pdev, &pdata, sizeof(pdata));
>>>> +
>>>> + ret = platform_device_add(region_ctx->pdev);
>>>> + if (ret) {
>>>> + pr_err("Fake FPGA region device add failed\n");
>>>> + platform_device_put(region_ctx->pdev);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + 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 0;
>>>> +}
>>>> +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;
>>>> +
>>>> + 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);
>>>> + }
>>>> +}
>>>> +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;
>>>> + int ret;
>>>> +
>>>> + priv = region_ctx->region->priv;
>>>> +
>>>> + ret = fpga_bridge_get_to_list(bridge->dev.parent, NULL,
>>>> + ®ion_ctx->region->bridge_list);
>>>> +
>>>> + if (priv->test && !ret)
>>>> + kunit_info(priv->test, "Bridge added to fake FPGA region %d\n",
>>>> + priv->id);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(fake_fpga_region_add_bridge);
>>>> +
>>>> +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;
>>>> + static int id_count;
>>>> +
>>>> + dev = &pdev->dev;
>>>> + pdata = dev_get_platdata(dev);
>>>> +
>>>> + 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);
>>>> +
>>>> + /*
>>>> + * No get_bridges() method since the bridges list is
>>>> + * pre-built using fake_fpga_region_add_bridge()
>>>> + */
>>>
>>> This is not the common use for drivers to associate the region & bridge,
>>> Better to realize the get_bridges() method.
>>
>> Initially, I was using a get_bridges() method to create the list of bridges
>> before each reconfiguration. However, this required having a "duplicated"
>> list of bridges in the context of the fake region low-level driver.
>>
>> Since I couldn't find a reason to keep a duplicate list of bridges in the
>> fake region driver, I decided then to change the approach and build the
>> list of bridges at device instantiation time.
>>
>> In my understanding, the approach of creating the list of bridges just
>> before reconfiguration with a get_bridges() method works best for the
>> OF case, where the topology is already encoded in the DT. I feel using
>> this approach on platforms without OF support would increase complexity
>> and create unnecessary duplication.
>
> I'm not fully get your point. My understanding is we don't have to
> always grab the bridge driver module if we don't reprogram. In many
> cases, we just work with the existing bitstream before Linux is started.
> So generally I prefer not to have an example that gets all bridges at
> initialization unless there is a real need.
The fake region can be used without bridges to model the scenario where
the FPGA is statically configured by the bootloader.
I was referring to the choice between building the bridge list of the
region (fpga_region->bridge_list) ahead of programming vs. just before
programming.
Currently, fake_fpga_region_add_bridge() attaches the bridge directly
to the bridge_list of the fpga_region struct.
Alternatively, I could change fake_fpga_region_add_bridge() to attach
the bridge to a secondary list in the low-level driver. The secondary
list would then be copied to the fpga_region->bridge_list by a
get_bridges() method just before programming.
However, I feel that using this approach would make test code more
complicated than necessary. Ideally, I would like to keep fake modules
as simple as possible.
Thanks,
Marco