From nobody Wed Dec 17 21:06:44 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0EBC5C77B70 for ; Mon, 17 Apr 2023 12:25:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231175AbjDQMZX (ORCPT ); Mon, 17 Apr 2023 08:25:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44244 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231126AbjDQMZP (ORCPT ); Mon, 17 Apr 2023 08:25:15 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC22B40C8 for ; Mon, 17 Apr 2023 05:23:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681734197; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nQ42dcQvki9IfHxndNIcUE+MqFzkjDsdNaPpQ6RElBw=; b=SrH/g2Zsv+px+GpTLZannPvOMjra3mckOiJ5jSy1VPuedu3yqeVAj2GIggcJtm6hKcils1 3oPqQsELVXyg52tp7/j+hyZNvOIn3bDdSn/WyCujhTdKyV0K1aji/sLumoi7cHtlHa5xwi pu3TErh0eZGSvgQ0DeGY6Qa8ilkWdfA= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-54-yUPKygymNWC2neA8LKp1Tw-1; Mon, 17 Apr 2023 08:23:16 -0400 X-MC-Unique: yUPKygymNWC2neA8LKp1Tw-1 Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-94a356c74e0so305877766b.2 for ; Mon, 17 Apr 2023 05:23:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681734195; x=1684326195; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nQ42dcQvki9IfHxndNIcUE+MqFzkjDsdNaPpQ6RElBw=; b=AJqjRp3vB89DD2H6nTLdElLpyZSCzJCGWgrUA5qjVzAA8eM1YPz0Ee9G+23QUfGpC6 ZIDB4ZDPhvERy6LwCzSGSEl3MTOL2ejckXmzGMFemquBpzcqpAXe6GrEAkvr2UJFMghz i8XbyvoWNKBaCPYbuQdCCElAKZnHxVE9gSG0GEOvCbusKPCUyxUa/7Kcq0p6X/nj2utb RA6iHUst2Rpt9NH9MOn7hJal6Bo42rh6dL++fKH5UB7Un67drR1abIhj45OpI4K6vl2b W9clBNtbEgMBZ0VlRCEDZfRAtu2M5FdHDYUicC58d9OlNrxlMrdM8CbNFC1USuGk10My X7hg== X-Gm-Message-State: AAQBX9fOUdKW34AU4ovdqIcDFQESEMrfwAS40L43mghc8ROiFoUtpyRU vTpustFefS54oBiU4AzreAZal70wWD9jCBHGsFC9/z0w63iRkjW+cwn/c+Ept+zFFyT002HhgTt qNZTFoQkT9dhBILNLnlO/7XA= X-Received: by 2002:aa7:c79a:0:b0:506:af22:1271 with SMTP id n26-20020aa7c79a000000b00506af221271mr1352658eds.0.1681734195519; Mon, 17 Apr 2023 05:23:15 -0700 (PDT) X-Google-Smtp-Source: AKy350b840itZTrJjuBKu9hRqJjRfC3ji23pWETtYNRuyF5SslQgbczA/v4Zo8zRfe2cFCeQmcatFQ== X-Received: by 2002:aa7:c79a:0:b0:506:af22:1271 with SMTP id n26-20020aa7c79a000000b00506af221271mr1352639eds.0.1681734195083; Mon, 17 Apr 2023 05:23:15 -0700 (PDT) Received: from klayman.. ([37.163.248.182]) by smtp.gmail.com with ESMTPSA id q21-20020aa7cc15000000b004fbf6b35a56sm5756618edt.76.2023.04.17.05.23.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Apr 2023 05:23:14 -0700 (PDT) From: Marco Pagani To: Moritz Fischer , Wu Hao , Xu Yilun , Tom Rix Cc: Marco Pagani , linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [RFC PATCH v4 1/4] fpga: add fake FPGA manager Date: Mon, 17 Apr 2023 14:23:05 +0200 Message-Id: <20230417122308.131453-2-marpagan@redhat.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230417122308.131453-1-marpagan@redhat.com> References: <20230417122308.131453-1-marpagan@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add fake FPGA manager platform driver with support functions. The driver checks the programming sequence using KUnit expectations. This module is part of the KUnit tests for the FPGA subsystem. Signed-off-by: Marco Pagani --- drivers/fpga/tests/fake-fpga-mgr.c | 386 +++++++++++++++++++++++++++++ drivers/fpga/tests/fake-fpga-mgr.h | 43 ++++ 2 files changed, 429 insertions(+) create mode 100644 drivers/fpga/tests/fake-fpga-mgr.c create mode 100644 drivers/fpga/tests/fake-fpga-mgr.h diff --git a/drivers/fpga/tests/fake-fpga-mgr.c b/drivers/fpga/tests/fake-f= pga-mgr.c new file mode 100644 index 000000000000..636df637b291 --- /dev/null +++ b/drivers/fpga/tests/fake-fpga-mgr.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the fake FPGA manager + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Marco Pagani + */ + +#include +#include +#include +#include +#include + +#include "fake-fpga-mgr.h" + +#define FAKE_FPGA_MGR_DEV_NAME "fake_fpga_mgr" + +#define FAKE_HEADER_BYTE 0x3f +#define FAKE_HEADER_SIZE FPGA_IMG_BLOCK + +struct fake_mgr_priv { + int rcfg_count; + bool op_parse_header; + bool op_write_init; + bool op_write; + bool op_write_sg; + bool op_write_complete; + struct kunit *test; +}; + +struct fake_mgr_data { + struct kunit *test; +}; + +static void check_header(struct kunit *test, const u8 *buf); + +static enum fpga_mgr_states op_state(struct fpga_manager *mgr) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA manager: state\n"); + + return FPGA_MGR_STATE_UNKNOWN; +} + +static u64 op_status(struct fpga_manager *mgr) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA manager: status\n"); + + return 0; +} + +static int op_parse_header(struct fpga_manager *mgr, struct fpga_image_inf= o *info, + const char *buf, size_t count) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) { + kunit_info(priv->test, "Fake FPGA manager: parse_header\n"); + + KUNIT_EXPECT_EQ(priv->test, mgr->state, + FPGA_MGR_STATE_PARSE_HEADER); + + check_header(priv->test, buf); + } + + priv->op_parse_header =3D true; + + return 0; +} + +static int op_write_init(struct fpga_manager *mgr, struct fpga_image_info = *info, + const char *buf, size_t count) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) { + kunit_info(priv->test, "Fake FPGA manager: write_init\n"); + + KUNIT_EXPECT_EQ(priv->test, mgr->state, + FPGA_MGR_STATE_WRITE_INIT); + } + + priv->op_write_init =3D true; + + return 0; +} + +static int op_write(struct fpga_manager *mgr, const char *buf, size_t coun= t) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) { + kunit_info(priv->test, "Fake FPGA manager: write\n"); + + KUNIT_EXPECT_EQ(priv->test, mgr->state, + FPGA_MGR_STATE_WRITE); + } + + priv->op_write =3D true; + + return 0; +} + +static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) { + kunit_info(priv->test, "Fake FPGA manager: write_sg\n"); + + KUNIT_EXPECT_EQ(priv->test, mgr->state, + FPGA_MGR_STATE_WRITE); + } + + priv->op_write_sg =3D true; + + return 0; +} + +static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_i= nfo *info) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) { + kunit_info(priv->test, "Fake FPGA manager: write_complete\n"); + + KUNIT_EXPECT_EQ(priv->test, mgr->state, + FPGA_MGR_STATE_WRITE_COMPLETE); + } + + priv->op_write_complete =3D true; + priv->rcfg_count++; + + return 0; +} + +static void op_fpga_remove(struct fpga_manager *mgr) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr->priv; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA manager: remove\n"); +} + +static const struct fpga_manager_ops fake_fpga_mgr_ops =3D { + .initial_header_size =3D FAKE_HEADER_SIZE, + .skip_header =3D false, + .state =3D op_state, + .status =3D op_status, + .parse_header =3D op_parse_header, + .write_init =3D op_write_init, + .write =3D op_write, + .write_sg =3D op_write_sg, + .write_complete =3D op_write_complete, + .fpga_remove =3D op_fpga_remove, +}; + +/** + * fake_fpga_mgr_register() - register a fake FPGA manager. + * @mgr_ctx: fake FPGA manager context data structure. + * @test: KUnit test context object. + * + * Return: pointer to a new fake FPGA manager on success, an ERR_PTR() + * encoded error code on failure. + */ +struct fake_fpga_mgr * +fake_fpga_mgr_register(struct kunit *test, struct device *parent) +{ + struct fake_fpga_mgr *mgr_ctx; + struct fake_mgr_data pdata; + int ret; + + mgr_ctx =3D kzalloc(sizeof(*mgr_ctx), GFP_KERNEL); + if (!mgr_ctx) { + ret =3D -ENOMEM; + goto err_mem; + } + + mgr_ctx->pdev =3D platform_device_alloc(FAKE_FPGA_MGR_DEV_NAME, + PLATFORM_DEVID_AUTO); + if (!mgr_ctx->pdev) { + pr_err("Fake FPGA manager device allocation failed\n"); + ret =3D -ENOMEM; + goto err_mem; + } + + pdata.test =3D test; + platform_device_add_data(mgr_ctx->pdev, &pdata, sizeof(pdata)); + + mgr_ctx->pdev->dev.parent =3D parent; + ret =3D platform_device_add(mgr_ctx->pdev); + if (ret) { + pr_err("Fake FPGA manager device add failed\n"); + goto err_pdev; + } + + mgr_ctx->mgr =3D platform_get_drvdata(mgr_ctx->pdev); + + if (test) + kunit_info(test, "Fake FPGA manager registered\n"); + + return mgr_ctx; + +err_pdev: + platform_device_put(mgr_ctx->pdev); + kfree(mgr_ctx); +err_mem: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_register); + +/** + * fake_fpga_mgr_unregister() - unregister a fake FPGA manager. + * @mgr_ctx: fake FPGA manager context data structure. + */ +void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx) +{ + struct fake_mgr_priv *priv; + struct kunit *test; + + if (!mgr_ctx) + return; + + priv =3D mgr_ctx->mgr->priv; + test =3D priv->test; + + if (mgr_ctx->pdev) { + platform_device_unregister(mgr_ctx->pdev); + if (test) + kunit_info(test, "Fake FPGA manager unregistered\n"); + } + + kfree(mgr_ctx); +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_unregister); + +/** + * fake_fpga_mgr_get_rcfg_count() - get the number of reconfigurations. + * @mgr_ctx: fake FPGA manager context data structure. + * + * Return: number of reconfigurations. + */ +int fake_fpga_mgr_get_rcfg_count(const struct fake_fpga_mgr *mgr_ctx) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr_ctx->mgr->priv; + + return priv->rcfg_count; +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_get_rcfg_count); + +/** + * fake_fpga_mgr_fill_header() - fill an image buffer with the test header. + * @buf: image buffer. + */ +void fake_fpga_mgr_fill_header(u8 *buf) +{ + int i; + + for (i =3D 0; i < FAKE_HEADER_SIZE; i++) + buf[i] =3D FAKE_HEADER_BYTE; +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_fill_header); + +static void check_header(struct kunit *test, const u8 *buf) +{ + int i; + + for (i =3D 0; i < FAKE_HEADER_SIZE; i++) + KUNIT_EXPECT_EQ(test, buf[i], FAKE_HEADER_BYTE); +} + +static void clear_op_flags(struct fake_mgr_priv *priv) +{ + priv->op_parse_header =3D false; + priv->op_write_init =3D false; + priv->op_write =3D false; + priv->op_write_sg =3D false; + priv->op_write_complete =3D false; +} + +/** + * fake_fpga_mgr_check_write_buf() - check if programming using a buffer s= ucceeded. + * @mgr_ctx: fake FPGA manager context data structure. + */ +void fake_fpga_mgr_check_write_buf(struct fake_fpga_mgr *mgr_ctx) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr_ctx->mgr->priv; + + if (priv->test) { + KUNIT_EXPECT_EQ(priv->test, priv->op_parse_header, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write_init, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write_complete, true); + } + + clear_op_flags(priv); +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_check_write_buf); + +/** + * fake_fpga_mgr_check_write_sgt() - check if programming using a s.g. tab= le succeeded. + * @mgr_ctx: fake FPGA manager context data structure. + */ +void fake_fpga_mgr_check_write_sgt(struct fake_fpga_mgr *mgr_ctx) +{ + struct fake_mgr_priv *priv; + + priv =3D mgr_ctx->mgr->priv; + + if (priv->test) { + KUNIT_EXPECT_EQ(priv->test, priv->op_parse_header, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write_init, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write_sg, true); + KUNIT_EXPECT_EQ(priv->test, priv->op_write_complete, true); + } + + clear_op_flags(priv); +} +EXPORT_SYMBOL_GPL(fake_fpga_mgr_check_write_sgt); + +static int fake_fpga_mgr_probe(struct platform_device *pdev) +{ + struct device *dev; + struct fake_mgr_priv *priv; + struct fake_mgr_data *pdata; + struct fpga_manager *mgr; + + dev =3D &pdev->dev; + pdata =3D dev_get_platdata(dev); + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (pdata) + priv->test =3D pdata->test; + + mgr =3D devm_fpga_mgr_register(dev, "Fake FPGA Manager", + &fake_fpga_mgr_ops, priv); + if (IS_ERR(mgr)) + return PTR_ERR(mgr); + + platform_set_drvdata(pdev, mgr); + + return 0; +} + +static struct platform_driver fake_fpga_mgr_drv =3D { + .driver =3D { + .name =3D FAKE_FPGA_MGR_DEV_NAME + }, + .probe =3D fake_fpga_mgr_probe, +}; + +module_platform_driver(fake_fpga_mgr_drv); + +MODULE_AUTHOR("Marco Pagani "); +MODULE_DESCRIPTION("Fake FPGA Manager"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/tests/fake-fpga-mgr.h b/drivers/fpga/tests/fake-f= pga-mgr.h new file mode 100644 index 000000000000..453672b5df72 --- /dev/null +++ b/drivers/fpga/tests/fake-fpga-mgr.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Header file for the fake FPGA manager + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Marco Pagani + */ + +#ifndef __FPGA_FAKE_MGR_H +#define __FPGA_FAKE_MGR_H + +#include +#include +#include + +#define FPGA_IMG_BLOCK 1024 + +/** + * struct fake_fpga_mgr - fake FPGA manager context data structure + * + * @mgr: FPGA manager. + * @pdev: platform device of the FPGA manager. + */ +struct fake_fpga_mgr { + struct fpga_manager *mgr; + struct platform_device *pdev; +}; + +struct fake_fpga_mgr * +fake_fpga_mgr_register(struct kunit *test, struct device *parent); + +void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx); + +int fake_fpga_mgr_get_rcfg_count(const struct fake_fpga_mgr *mgr_ctx); + +void fake_fpga_mgr_fill_header(u8 *buf); + +void fake_fpga_mgr_check_write_buf(struct fake_fpga_mgr *mgr_ctx); + +void fake_fpga_mgr_check_write_sgt(struct fake_fpga_mgr *mgr_ctx); + +#endif /* __FPGA_FAKE_MGR_H */ --=20 2.39.2 From nobody Wed Dec 17 21:06:44 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40E0FC77B70 for ; Mon, 17 Apr 2023 12:25:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231136AbjDQMZS (ORCPT ); Mon, 17 Apr 2023 08:25:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44174 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230502AbjDQMZN (ORCPT ); Mon, 17 Apr 2023 08:25:13 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 61B3C44B0 for ; Mon, 17 Apr 2023 05:23:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681734199; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=p9EM7KUyJwVaEwxyA/Y+8HQEJUTbAzizsc3Hf5VvwRo=; b=MYCAO6rWM9+NSl++7kt7jsSbm16sZdyML4PeI1DohJtQNEHlXEttr1EN7HQzZf4fq737FB ZbQ1j/mPZRYWEpUIsFb6JeAjcip/w9JGZWgvcOEKDJzI8lBwyJ4c7ebDce9CATGlbAQgvQ SxuX6y/xwe+lcd9JfdOiFwDcCg5lAQQ= Received: from mail-ed1-f70.google.com (mail-ed1-f70.google.com [209.85.208.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-443-wALICmA5OXedtF8MT-t0Dg-1; Mon, 17 Apr 2023 08:23:17 -0400 X-MC-Unique: wALICmA5OXedtF8MT-t0Dg-1 Received: by mail-ed1-f70.google.com with SMTP id 4fb4d7f45d1cf-505149e1a4eso10315652a12.1 for ; Mon, 17 Apr 2023 05:23:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681734196; x=1684326196; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=p9EM7KUyJwVaEwxyA/Y+8HQEJUTbAzizsc3Hf5VvwRo=; b=baVZ72FcEvoLeuZaSowEKZTddnGsseyfjqOYlrw5EXa2zfQhMZElglvRI5uSB2Hn1e EnXjflgh+pPbsyx6xt6EA4KkmBXMZIKjWWA/25WDWW7GMpSf+VfDsoMUVWRdCzFU9fNm WtvLjf083NTWDbXts6dfav0aSKmrFRMGTIHvw6hN0VuK88AqGTdSdvb76FtaHkwsRoFv upB19Mb8/sJD91GqQ/hOceLhptynX97CeImBMHaHB5it92QbKHYEmbVz5jrCOtzKqvs4 DWTy3WGCj7lbeWA5iUD7hDy3dLVInXj6FkH1Zr7HMaCsAO6v+j5+Ss3mDrULnId/v1ER aINw== X-Gm-Message-State: AAQBX9dUuoYgRlOYj5W6cAOi9eentYcPZkoFszalP/pT6b06mR7olzsq dSxuHIpaTAhGQKLGXLIkVTODH0bRJytAyXrNWCJwI8i1VBVYA8PSR5yTHn2T04T+U9T0w8doSVi 3Oan3N/b5LqfnuOSv6Wbjbdw= X-Received: by 2002:aa7:cf88:0:b0:500:47ed:9784 with SMTP id z8-20020aa7cf88000000b0050047ed9784mr12990287edx.14.1681734196733; Mon, 17 Apr 2023 05:23:16 -0700 (PDT) X-Google-Smtp-Source: AKy350bcV5yClC5o+iqbcAU05lKyT1d8TcCYeG2x9TZrpFENRu8PF08g4fj64YjFI1bngK2VwI1qVQ== X-Received: by 2002:aa7:cf88:0:b0:500:47ed:9784 with SMTP id z8-20020aa7cf88000000b0050047ed9784mr12990267edx.14.1681734196373; Mon, 17 Apr 2023 05:23:16 -0700 (PDT) Received: from klayman.. ([37.163.248.182]) by smtp.gmail.com with ESMTPSA id q21-20020aa7cc15000000b004fbf6b35a56sm5756618edt.76.2023.04.17.05.23.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Apr 2023 05:23:16 -0700 (PDT) From: Marco Pagani To: Moritz Fischer , Wu Hao , Xu Yilun , Tom Rix Cc: Marco Pagani , linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [RFC PATCH v4 2/4] fpga: add fake FPGA bridge Date: Mon, 17 Apr 2023 14:23:06 +0200 Message-Id: <20230417122308.131453-3-marpagan@redhat.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230417122308.131453-1-marpagan@redhat.com> References: <20230417122308.131453-1-marpagan@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add fake FPGA bridge driver with support functions. The driver includes a counter for the number of switching cycles. This module is part of the KUnit tests for the FPGA subsystem. Signed-off-by: Marco Pagani --- drivers/fpga/tests/fake-fpga-bridge.c | 242 ++++++++++++++++++++++++++ drivers/fpga/tests/fake-fpga-bridge.h | 36 ++++ 2 files changed, 278 insertions(+) create mode 100644 drivers/fpga/tests/fake-fpga-bridge.c create mode 100644 drivers/fpga/tests/fake-fpga-bridge.h diff --git a/drivers/fpga/tests/fake-fpga-bridge.c b/drivers/fpga/tests/fak= e-fpga-bridge.c new file mode 100644 index 000000000000..6a62c04a75c0 --- /dev/null +++ b/drivers/fpga/tests/fake-fpga-bridge.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the fake FPGA bridge + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Marco Pagani + */ + +#include +#include +#include +#include +#include + +#include "fake-fpga-bridge.h" + +#define FAKE_FPGA_BRIDGE_DEV_NAME "fake_fpga_bridge" + +struct fake_bridge_priv { + int id; + bool enable; + int cycles_count; + struct kunit *test; +}; + +struct fake_bridge_data { + struct kunit *test; +}; + +static int op_enable_show(struct fpga_bridge *bridge) +{ + struct fake_bridge_priv *priv; + + priv =3D bridge->priv; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA bridge %d: enable_show\n", + priv->id); + + return priv->enable; +} + +static int op_enable_set(struct fpga_bridge *bridge, bool enable) +{ + struct fake_bridge_priv *priv; + + priv =3D bridge->priv; + + if (enable && !priv->enable) + priv->cycles_count++; + + priv->enable =3D enable; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA bridge %d: enable_set: %d\n", + priv->id, enable); + + return 0; +} + +static void op_remove(struct fpga_bridge *bridge) +{ + struct fake_bridge_priv *priv; + + priv =3D bridge->priv; + + if (priv->test) + kunit_info(priv->test, "Fake FPGA bridge: remove\n"); +} + +static const struct fpga_bridge_ops fake_fpga_bridge_ops =3D { + .enable_show =3D op_enable_show, + .enable_set =3D op_enable_set, + .fpga_bridge_remove =3D op_remove, +}; + +/** + * fake_fpga_bridge_register() - register a fake FPGA bridge. + * @parent: parent device. + * @test: KUnit test context object. + * + * Return: pointer to a new fake FPGA bridge on success, an ERR_PTR() + * encoded error code on failure. + */ +struct fake_fpga_bridge * +fake_fpga_bridge_register(struct device *parent, struct kunit *test) +{ + struct fake_fpga_bridge *bridge_ctx; + struct fake_bridge_data pdata; + struct fake_bridge_priv *priv; + int ret; + + bridge_ctx =3D kzalloc(sizeof(*bridge_ctx), GFP_KERNEL); + if (!bridge_ctx) { + ret =3D -ENOMEM; + goto err_mem; + } + + bridge_ctx->pdev =3D platform_device_alloc(FAKE_FPGA_BRIDGE_DEV_NAME, + PLATFORM_DEVID_AUTO); + if (!bridge_ctx->pdev) { + pr_err("Fake FPGA bridge device allocation failed\n"); + ret =3D -ENOMEM; + goto err_mem; + } + + pdata.test =3D test; + platform_device_add_data(bridge_ctx->pdev, &pdata, sizeof(pdata)); + + bridge_ctx->pdev->dev.parent =3D parent; + ret =3D platform_device_add(bridge_ctx->pdev); + if (ret) { + pr_err("Fake FPGA bridge device add failed\n"); + goto err_pdev; + } + + bridge_ctx->bridge =3D platform_get_drvdata(bridge_ctx->pdev); + + if (test) { + priv =3D bridge_ctx->bridge->priv; + kunit_info(test, "Fake FPGA bridge %d registered\n", priv->id); + } + + return bridge_ctx; + +err_pdev: + platform_device_put(bridge_ctx->pdev); + kfree(bridge_ctx); +err_mem: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(fake_fpga_bridge_register); + +/** + * fake_fpga_bridge_unregister() - unregister a fake FPGA bridge. + * @bridge_ctx: fake FPGA bridge context data structure. + */ +void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx) +{ + struct fake_bridge_priv *priv; + struct kunit *test; + int id; + + if (!bridge_ctx) + return; + + priv =3D bridge_ctx->bridge->priv; + test =3D priv->test; + id =3D priv->id; + + if (bridge_ctx->pdev) { + platform_device_unregister(bridge_ctx->pdev); + if (test) + kunit_info(test, "Fake FPGA bridge %d unregistered\n", id); + } + + kfree(bridge_ctx); +} +EXPORT_SYMBOL_GPL(fake_fpga_bridge_unregister); + +/** + * fake_fpga_bridge_get_state() - get state of a fake FPGA bridge. + * @bridge_ctx: fake FPGA bridge context data structure. + * + * Return: 1 if the bridge is enabled, 0 if disabled. + */ +int fake_fpga_bridge_get_state(const struct fake_fpga_bridge *bridge_ctx) +{ + return bridge_ctx->bridge->br_ops->enable_show(bridge_ctx->bridge); +} +EXPORT_SYMBOL_GPL(fake_fpga_bridge_get_state); + +/** + * fake_fpga_bridge_get_cycles_count() - get the number of switching cycle= s. + * @bridge_ctx: fake FPGA bridge context data structure. + * + * Return: number of switching cycles. + */ +int fake_fpga_bridge_get_cycles_count(const struct fake_fpga_bridge *bridg= e_ctx) +{ + struct fake_bridge_priv *priv; + + priv =3D bridge_ctx->bridge->priv; + + return priv->cycles_count; +} +EXPORT_SYMBOL_GPL(fake_fpga_bridge_get_cycles_count); + +static int fake_fpga_bridge_probe(struct platform_device *pdev) +{ + struct device *dev; + struct fpga_bridge *bridge; + struct fake_bridge_data *pdata; + struct fake_bridge_priv *priv; + static int id_count; + + dev =3D &pdev->dev; + pdata =3D dev_get_platdata(dev); + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->id =3D id_count++; + priv->enable =3D true; + + if (pdata) + priv->test =3D pdata->test; + + bridge =3D fpga_bridge_register(dev, "Fake FPGA Bridge", + &fake_fpga_bridge_ops, priv); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + platform_set_drvdata(pdev, bridge); + + return 0; +} + +static int fake_fpga_bridge_remove(struct platform_device *pdev) +{ + struct fpga_bridge *bridge =3D platform_get_drvdata(pdev); + + fpga_bridge_unregister(bridge); + + return 0; +} + +static struct platform_driver fake_fpga_bridge_drv =3D { + .driver =3D { + .name =3D FAKE_FPGA_BRIDGE_DEV_NAME + }, + .probe =3D fake_fpga_bridge_probe, + .remove =3D fake_fpga_bridge_remove, +}; + +module_platform_driver(fake_fpga_bridge_drv); + +MODULE_AUTHOR("Marco Pagani "); +MODULE_DESCRIPTION("Fake FPGA Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/tests/fake-fpga-bridge.h b/drivers/fpga/tests/fak= e-fpga-bridge.h new file mode 100644 index 000000000000..a5acfdf18056 --- /dev/null +++ b/drivers/fpga/tests/fake-fpga-bridge.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Header file for the fake FPGA bridge + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Marco Pagani + */ + +#ifndef __FPGA_FAKE_BRIDGE_H +#define __FPGA_FAKE_BRIDGE_H + +#include +#include + +/** + * struct fake_fpga_bridge - fake FPGA bridge context data structure + * + * @bridge: FPGA bridge. + * @pdev: platform device of the FPGA bridge. + */ +struct fake_fpga_bridge { + struct fpga_bridge *bridge; + struct platform_device *pdev; +}; + +struct fake_fpga_bridge * +fake_fpga_bridge_register(struct device *parent, struct kunit *test); + +void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx); + +int fake_fpga_bridge_get_state(const struct fake_fpga_bridge *bridge_ctx); + +int fake_fpga_bridge_get_cycles_count(const struct fake_fpga_bridge *bridg= e_ctx); + +#endif /* __FPGA_FAKE_BRIDGE_H */ --=20 2.39.2 From nobody Wed Dec 17 21:06:44 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E03FC77B70 for ; Mon, 17 Apr 2023 12:25:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229887AbjDQMZP (ORCPT ); Mon, 17 Apr 2023 08:25:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44180 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229793AbjDQMZL (ORCPT ); Mon, 17 Apr 2023 08:25:11 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AC53359FE for ; Mon, 17 Apr 2023 05:24:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681734200; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ou06vBW/0QyFtGlD5ya/Ii/hKgm9cYXdnbTozs3jPKM=; b=VgQmiszvmLYnPrINLJ8qqWjFfK7KT/GJvO8HztbRaQnzkdKvfqfP1VrtVgKet9uIzNLHPD fNmN2+mm44bvYVAIkUG78aWZpiV9F+sLwt8oMI7nGlh0cYtYDumPI/s5CCKiy37AN2nGFr yAz268YoSybhG1DmYBp2oyQxuXy27Ug= Received: from mail-ed1-f72.google.com (mail-ed1-f72.google.com [209.85.208.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-202-lq8C6mUjOfiU6Nso7Ge61Q-1; Mon, 17 Apr 2023 08:23:19 -0400 X-MC-Unique: lq8C6mUjOfiU6Nso7Ge61Q-1 Received: by mail-ed1-f72.google.com with SMTP id 4fb4d7f45d1cf-5067c88a429so1735255a12.3 for ; Mon, 17 Apr 2023 05:23:19 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681734198; x=1684326198; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ou06vBW/0QyFtGlD5ya/Ii/hKgm9cYXdnbTozs3jPKM=; b=XBAx8KfUcxl13o4Be8qHLl+RexbbbeD/hy6TvtVmpSBHWLimMuz4CdT2/CiNDDVVuL rbtdcK/csJUIUZtfUnVyz/jf5gitFJk+QKswN8enUG7yTnoTSdFWArYeXwYgFiybb06l dUa8pzHLorqtnOfVMiuqNpg++b+ZjFZlrFFs/DuDhGOsrFg4OBJt/shuNi7gpQ4E/O+X uuwUxVCk+etDxU+wBA1BK90vUXgGmYdBvrXUsQL+8PLaFgsQkJap3q3G00fcXo9YfDUr xkqRbBAkSGP6M0AXBS63D2EJty4A5m9IY6WZ7mrrnCyvdidKP2IFSlmn2p7UjDRka3UN Sdlw== X-Gm-Message-State: AAQBX9d0g5s2wrIX84fDGGMbddoEusRoK957P2QRjlLNK744Zbc7NQOq lw2QYE6VNqWCDFHwh1TVq0KT4Mru/6WVIah5Mfygm8ZyeCuQIXhAtBF75fkKy81qfyjFk+kgAWT Wege55mb+QSu9DFp67utR+WM= X-Received: by 2002:a05:6402:138b:b0:506:72fc:8dd7 with SMTP id b11-20020a056402138b00b0050672fc8dd7mr12514395edv.24.1681734198136; Mon, 17 Apr 2023 05:23:18 -0700 (PDT) X-Google-Smtp-Source: AKy350ZSttHT1nZjkXCWrkAxDdPs36P9jYt8i38CJc2oE2JCR7/NExrkmtYRfl1A0Ru1uyrc2PrEBg== X-Received: by 2002:a05:6402:138b:b0:506:72fc:8dd7 with SMTP id b11-20020a056402138b00b0050672fc8dd7mr12514377edv.24.1681734197796; Mon, 17 Apr 2023 05:23:17 -0700 (PDT) Received: from klayman.. ([37.163.248.182]) by smtp.gmail.com with ESMTPSA id q21-20020aa7cc15000000b004fbf6b35a56sm5756618edt.76.2023.04.17.05.23.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Apr 2023 05:23:17 -0700 (PDT) From: Marco Pagani To: Moritz Fischer , Wu Hao , Xu Yilun , Tom Rix Cc: Marco Pagani , linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [RFC PATCH v4 3/4] fpga: add fake FPGA region Date: Mon, 17 Apr 2023 14:23:07 +0200 Message-Id: <20230417122308.131453-4-marpagan@redhat.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230417122308.131453-1-marpagan@redhat.com> References: <20230417122308.131453-1-marpagan@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 --- 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/fak= e-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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#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() enco= ded + * 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 =3D kzalloc(sizeof(*region_ctx), GFP_KERNEL); + if (!region_ctx) { + ret =3D -ENOMEM; + goto err_mem; + } + + region_ctx->pdev =3D 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 =3D -ENOMEM; + goto err_mem; + } + + pdata.mgr =3D mgr; + pdata.test =3D test; + platform_device_add_data(region_ctx->pdev, &pdata, sizeof(pdata)); + + region_ctx->pdev->dev.parent =3D parent; + ret =3D platform_device_add(region_ctx->pdev); + if (ret) { + pr_err("Fake FPGA region device add failed\n"); + goto err_pdev; + } + + region_ctx->region =3D platform_get_drvdata(region_ctx->pdev); + + if (test) { + priv =3D 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 =3D region_ctx->region->priv; + test =3D priv->test; + id =3D 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 =3D region_ctx->region->priv; + + elem =3D 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 =3D 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); + +int fake_fpga_region_program(struct fake_fpga_region *region_ctx) +{ + int ret; + + ret =3D 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 =3D region->priv; + + /* Copy the list of bridges from the private context to the region */ + list_for_each_entry(elem, &priv->bridge_list, node) { + ret =3D 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 =3D &pdev->dev; + pdata =3D dev_get_platdata(dev); + + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -EINVAL; + } + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mgr =3D fpga_mgr_get(pdata->mgr->dev.parent); + if (IS_ERR(mgr)) + return PTR_ERR(mgr); + + INIT_LIST_HEAD(&priv->bridge_list); + priv->id =3D id_count++; + priv->test =3D pdata->test; + + memset(&info, 0, sizeof(info)); + info.priv =3D priv; + info.mgr =3D mgr; + info.get_bridges =3D fake_region_get_bridges; + + region =3D 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 =3D platform_get_drvdata(pdev); + struct fpga_manager *mgr =3D region->mgr; + + fpga_mgr_put(mgr); + fpga_region_unregister(region); + + return 0; +} + +static struct platform_driver fake_fpga_region_drv =3D { + .driver =3D { + .name =3D FAKE_FPGA_REGION_DEV_NAME + }, + .probe =3D fake_fpga_region_probe, + .remove =3D fake_fpga_region_remove, +}; + +module_platform_driver(fake_fpga_region_drv); + +MODULE_AUTHOR("Marco Pagani "); +MODULE_DESCRIPTION("Fake FPGA Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/tests/fake-fpga-region.h b/drivers/fpga/tests/fak= e-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 + */ + +#ifndef __FPGA_FAKE_RGN_H +#define __FPGA_FAKE_RGN_H + +#include +#include +#include +#include + +/** + * 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 */ --=20 2.39.2 From nobody Wed Dec 17 21:06:44 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B02D1C77B70 for ; Mon, 17 Apr 2023 12:25:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231182AbjDQMZg (ORCPT ); Mon, 17 Apr 2023 08:25:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44402 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231146AbjDQMZV (ORCPT ); Mon, 17 Apr 2023 08:25:21 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C7B927682 for ; Mon, 17 Apr 2023 05:24:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681734202; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=g1TfEl15TvyQuW0Jd2k6AgOgc4iivRGBLaeKw9XMy7A=; b=D/ttu2pMblpKpMORiyLb4RUA/AkN/kSnu6Z1N92wmM4TyltlEwyWyDzH73/l49P19kprw1 kRxdRBCamd3vQK2WuuXTMB5dzpeW+d6eFngsJoEWKP7SJenUO8uaInY96sXDnAVjV/j39v 2KZjgSPcjPnltfL3X3hJeZ/kmPGgmJc= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-436-MiUj5xV-MhKlIaVlHFI4Fw-1; Mon, 17 Apr 2023 08:23:21 -0400 X-MC-Unique: MiUj5xV-MhKlIaVlHFI4Fw-1 Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-94ee1fc1233so100205466b.1 for ; Mon, 17 Apr 2023 05:23:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681734199; x=1684326199; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g1TfEl15TvyQuW0Jd2k6AgOgc4iivRGBLaeKw9XMy7A=; b=TKZ4i+OsDsHgtCF/agi8fZ9rSQQTLtajPutBlcspN26ypF7+DgKr+kkDzqOhtJfJk3 ial/CNEeqnEd13s/aoVol2TS0C66JkDr49EO0M+kF8KCq2DXD4w6XFAoOAq9poZOoDHZ UOTqkeTw19tc/JZWmHnlMRnzEXxPj7WlA3iJTELMp/rngJweYUAPi0qirKZWB40niA+G 4nn1Ig9u7JIYRhoEqCtw10JzUlI85+LISK7/LzeeFIekwiabHzHek+HxAFYCjKQ5N9KW 7E3VqXPMeqh98MdUpjszBJv2QSnZKwVAT/WHEBeQuycOcUSRfeTaF6/+xMwZQQnk2+5G yTbw== X-Gm-Message-State: AAQBX9cv7QhJa4fKTBxPihm1WLGGwrM0ZYyBVoZ3c8sPDwu0fFEmNkcY OKuGfXEqHDmzE6hOL7vI5kS3n9t7JA35ukyDg+/3rHqBftjhW72aqNrsFJwNZFEIkdD52U9Tr1S KvLDmfvqmxn6aKRzaQSUHBpL4VT8h6lY= X-Received: by 2002:a50:fb97:0:b0:501:c839:cf91 with SMTP id e23-20020a50fb97000000b00501c839cf91mr14133688edq.6.1681734199497; Mon, 17 Apr 2023 05:23:19 -0700 (PDT) X-Google-Smtp-Source: AKy350aEGGmMDdQ4m5HWFGXByQUBMZS1a5K/u5Rz4Qa6Qz7RH9t7kFmoP7/x7octEzYxJ0quiOTnmQ== X-Received: by 2002:a50:fb97:0:b0:501:c839:cf91 with SMTP id e23-20020a50fb97000000b00501c839cf91mr14133670edq.6.1681734199215; Mon, 17 Apr 2023 05:23:19 -0700 (PDT) Received: from klayman.. ([37.163.248.182]) by smtp.gmail.com with ESMTPSA id q21-20020aa7cc15000000b004fbf6b35a56sm5756618edt.76.2023.04.17.05.23.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Apr 2023 05:23:18 -0700 (PDT) From: Marco Pagani To: Moritz Fischer , Wu Hao , Xu Yilun , Tom Rix Cc: Marco Pagani , linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [RFC PATCH v4 4/4] fpga: add initial KUnit test suites Date: Mon, 17 Apr 2023 14:23:08 +0200 Message-Id: <20230417122308.131453-5-marpagan@redhat.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230417122308.131453-1-marpagan@redhat.com> References: <20230417122308.131453-1-marpagan@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Introduce initial KUnit tests for the FPGA subsystem. Tests are organized into three test suites. The first suite tests the FPGA Manager. The second suite tests the FPGA Bridge. Finally, the last test suite models a complete FPGA platform and tests static and partial reconfiguration. Signed-off-by: Marco Pagani --- drivers/fpga/Kconfig | 2 + drivers/fpga/Makefile | 3 + drivers/fpga/tests/.kunitconfig | 5 + drivers/fpga/tests/Kconfig | 11 + drivers/fpga/tests/Makefile | 6 + drivers/fpga/tests/fpga-test.c | 479 ++++++++++++++++++++++++++++++++ 6 files changed, 506 insertions(+) create mode 100644 drivers/fpga/tests/.kunitconfig create mode 100644 drivers/fpga/tests/Kconfig create mode 100644 drivers/fpga/tests/Makefile create mode 100644 drivers/fpga/tests/fpga-test.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 0a00763b9f28..2f689ac4ba3a 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -276,4 +276,6 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI FPGA manager driver support for Lattice FPGAs programming over slave SPI sysCONFIG interface. =20 +source "drivers/fpga/tests/Kconfig" + endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 72e554b4d2f7..352a2612623e 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -55,3 +55,6 @@ obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000) +=3D dfl-n300= 0-nios.o =20 # Drivers for FPGAs which implement DFL obj-$(CONFIG_FPGA_DFL_PCI) +=3D dfl-pci.o + +# KUnit tests +obj-$(CONFIG_FPGA_KUNIT_TESTS) +=3D tests/ diff --git a/drivers/fpga/tests/.kunitconfig b/drivers/fpga/tests/.kunitcon= fig new file mode 100644 index 000000000000..a1c2a2974c39 --- /dev/null +++ b/drivers/fpga/tests/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=3Dy +CONFIG_FPGA=3Dy +CONFIG_FPGA_REGION=3Dy +CONFIG_FPGA_BRIDGE=3Dy +CONFIG_FPGA_KUNIT_TESTS=3Dy diff --git a/drivers/fpga/tests/Kconfig b/drivers/fpga/tests/Kconfig new file mode 100644 index 000000000000..1cbea75dd29b --- /dev/null +++ b/drivers/fpga/tests/Kconfig @@ -0,0 +1,11 @@ +config FPGA_KUNIT_TESTS + tristate "KUnit test for the FPGA subsystem" if !KUNIT_ALL_TESTS + depends on FPGA && FPGA_REGION && FPGA_BRIDGE && KUNIT + default KUNIT_ALL_TESTS + help + This builds unit tests for the FPGA subsystem + + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in Documentation/dev-too= ls/kunit/. + + If unsure, say N. diff --git a/drivers/fpga/tests/Makefile b/drivers/fpga/tests/Makefile new file mode 100644 index 000000000000..0b052570659b --- /dev/null +++ b/drivers/fpga/tests/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_FPGA_KUNIT_TESTS) +=3D fake-fpga-mgr.o +obj-$(CONFIG_FPGA_KUNIT_TESTS) +=3D fake-fpga-region.o +obj-$(CONFIG_FPGA_KUNIT_TESTS) +=3D fake-fpga-bridge.o +obj-$(CONFIG_FPGA_KUNIT_TESTS) +=3D fpga-test.o diff --git a/drivers/fpga/tests/fpga-test.c b/drivers/fpga/tests/fpga-test.c new file mode 100644 index 000000000000..52e71ce07b27 --- /dev/null +++ b/drivers/fpga/tests/fpga-test.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for the FPGA subsystem + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Marco Pagani + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "fake-fpga-region.h" +#include "fake-fpga-bridge.h" +#include "fake-fpga-mgr.h" + +#define STATIC_IMG_BLOCKS 16 +#define STATIC_IMG_SIZE (FPGA_IMG_BLOCK * STATIC_IMG_BLOCKS) + +#define PARTIAL_IMG_BLOCKS 4 +#define PARTIAL_IMG_SIZE (FPGA_IMG_BLOCK * PARTIAL_IMG_BLOCKS) + +/** + * buf_img_alloc() - Allocate a fake FPGA image using a buffer. + * @test: KUnit test context object. + * @dev: owning device. + * @size: image size. + * + * Return: pointer to a struct fpga_image_info or NULL on failure. + */ +static struct fpga_image_info *buf_img_alloc(struct kunit *test, struct de= vice *dev, + size_t size) +{ + struct fpga_image_info *img_info; + char *img_buf; + + img_buf =3D kunit_kzalloc(test, size, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf); + fake_fpga_mgr_fill_header(img_buf); + + img_info =3D fpga_image_info_alloc(dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); + + img_info->count =3D size; + img_info->buf =3D img_buf; + + kunit_info(test, "FPGA image allocated in a buffer, size: %zu\n", size); + + return img_info; +} + +/** + * sgt_img_alloc() - Allocate a fake FPGA image using a scatter gather tab= le. + * @test: KUnit test context object. + * @dev: owning device. + * @size: image size. + * + * Return: pointer to a struct fpga_image_info or NULL on failure. + */ +static struct fpga_image_info *sgt_img_alloc(struct kunit *test, struct de= vice *dev, + size_t size) +{ + struct fpga_image_info *img_info; + char *img_buf; + struct sg_table *sgt; + int ret; + + img_buf =3D kunit_kzalloc(test, size, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf); + fake_fpga_mgr_fill_header(img_buf); + + sgt =3D kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL); + ret =3D sg_alloc_table(sgt, 1, GFP_KERNEL); + KUNIT_ASSERT_EQ(test, ret, 0); + sg_init_one(sgt->sgl, img_buf, size); + + img_info =3D fpga_image_info_alloc(dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); + + img_info->sgt =3D sgt; + + kunit_info(test, "FPGA image allocated in a scatter gather table, size: %= zu\n", + size); + + return img_info; +} + +/** + * img_free() - Free a fake FPGA image + * @img_info: fpga image information struct. + * + */ +static void img_free(struct fpga_image_info *img_info) +{ + if (!img_info) + return; + + if (img_info->sgt) + sg_free_table(img_info->sgt); + + fpga_image_info_free(img_info); +} + +static int fpga_mgr_test_init(struct kunit *test) +{ + struct fake_fpga_mgr *mgr_ctx; + + mgr_ctx =3D fake_fpga_mgr_register(test, NULL); + KUNIT_ASSERT_FALSE(test, IS_ERR(mgr_ctx)); + + test->priv =3D mgr_ctx; + + return 0; +} + +static void fpga_mgr_test_img_load_buf(struct kunit *test) +{ + struct fake_fpga_mgr *mgr_ctx; + struct fpga_image_info *img_info; + int ret; + + mgr_ctx =3D test->priv; + + /* Allocate an FPGA image using a buffer */ + img_info =3D buf_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); + + KUNIT_EXPECT_EQ(test, 0, fake_fpga_mgr_get_rcfg_count(mgr_ctx)); + + ret =3D fpga_mgr_load(mgr_ctx->mgr, img_info); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_buf(mgr_ctx); + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_mgr_get_rcfg_count(mgr_ctx)); + + img_free(img_info); +} + +static void fpga_mgr_test_img_load_sgt(struct kunit *test) +{ + struct fake_fpga_mgr *mgr_ctx; + struct fpga_image_info *img_info; + int ret; + + mgr_ctx =3D test->priv; + + /* Allocate an FPGA image using a scatter gather table */ + img_info =3D sgt_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); + + KUNIT_EXPECT_EQ(test, 0, fake_fpga_mgr_get_rcfg_count(mgr_ctx)); + + ret =3D fpga_mgr_load(mgr_ctx->mgr, img_info); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_sgt(mgr_ctx); + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_mgr_get_rcfg_count(mgr_ctx)); + + img_free(img_info); +} + +static void fpga_mgr_test_exit(struct kunit *test) +{ + struct fake_fpga_mgr *mgr_ctx; + + mgr_ctx =3D test->priv; + + if (mgr_ctx) + fake_fpga_mgr_unregister(mgr_ctx); +} + +static struct kunit_case fpga_mgr_test_cases[] =3D { + KUNIT_CASE(fpga_mgr_test_img_load_buf), + KUNIT_CASE(fpga_mgr_test_img_load_sgt), + {} +}; + +static struct kunit_suite fpga_mgr_suite =3D { + .name =3D "fpga_mgr", + .init =3D fpga_mgr_test_init, + .exit =3D fpga_mgr_test_exit, + .test_cases =3D fpga_mgr_test_cases, +}; + +static int fpga_bridge_test_init(struct kunit *test) +{ + struct fake_fpga_bridge *bridge_ctx; + + bridge_ctx =3D fake_fpga_bridge_register(NULL, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_ctx)); + + test->priv =3D bridge_ctx; + + return 0; +} + +static void fpga_bridge_test_exit(struct kunit *test) +{ + struct fake_fpga_bridge *bridge_ctx; + + bridge_ctx =3D test->priv; + + if (bridge_ctx) + fake_fpga_bridge_unregister(bridge_ctx); +} + +static void fpga_bridge_test_toggle(struct kunit *test) +{ + struct fake_fpga_bridge *bridge_ctx; + + bridge_ctx =3D test->priv; + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(bridge_ctx)); + + fpga_bridge_disable(bridge_ctx->bridge); + KUNIT_EXPECT_EQ(test, 0, fake_fpga_bridge_get_state(bridge_ctx)); + + fpga_bridge_enable(bridge_ctx->bridge); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(bridge_ctx)); +} + +static void fpga_bridge_test_get_put_list(struct kunit *test) +{ + struct list_head bridge_list; + struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx; + int ret; + + bridge_0_ctx =3D test->priv; + + /* Register another bridge for this test */ + bridge_1_ctx =3D fake_fpga_bridge_register(NULL, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx)); + + INIT_LIST_HEAD(&bridge_list); + + /* Get bridge_0 and add it to the list */ + ret =3D fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL, + &bridge_list); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge, + list_first_entry_or_null(&bridge_list, struct fpga_bridge, node)); + + /* Get bridge_1 and add it to the list */ + ret =3D fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL, + &bridge_list); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge, + list_first_entry_or_null(&bridge_list, struct fpga_bridge, node)); + + /* Put and remove both bridges from the list */ + fpga_bridges_put(&bridge_list); + + KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list)); + + fake_fpga_bridge_unregister(bridge_1_ctx); +} + +static struct kunit_case fpga_bridge_test_cases[] =3D { + KUNIT_CASE(fpga_bridge_test_toggle), + KUNIT_CASE(fpga_bridge_test_get_put_list), + {} +}; + +static struct kunit_suite fpga_bridge_suite =3D { + .name =3D "fpga_bridge", + .init =3D fpga_bridge_test_init, + .exit =3D fpga_bridge_test_exit, + .test_cases =3D fpga_bridge_test_cases, +}; + +struct fpga_base_ctx { + /* + * Base FPGA layout consisting of a single region + * controlled by a bridge and the FPGA manager + */ + struct fake_fpga_mgr *mgr_ctx; + struct fake_fpga_bridge *bridge_ctx; + struct fake_fpga_region *region_ctx; +}; + +static int fpga_test_init(struct kunit *test) +{ + struct fpga_base_ctx *base_ctx; + int ret; + + base_ctx =3D kunit_kzalloc(test, sizeof(*base_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, base_ctx); + test->priv =3D base_ctx; + + /* Build the base FPGA layout */ + base_ctx->mgr_ctx =3D fake_fpga_mgr_register(test, NULL); + KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->mgr_ctx)); + + base_ctx->bridge_ctx =3D fake_fpga_bridge_register(NULL, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->bridge_ctx)); + + /* The base region a child of the base bridge */ + base_ctx->region_ctx =3D fake_fpga_region_register(base_ctx->mgr_ctx->mgr, + &base_ctx->bridge_ctx->bridge->dev, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->region_ctx)); + + ret =3D fake_fpga_region_add_bridge(base_ctx->region_ctx, base_ctx->bridg= e_ctx->bridge); + KUNIT_ASSERT_EQ(test, ret, 0); + + kunit_info(test, "FPGA base system built\n"); + + KUNIT_EXPECT_EQ(test, 0, fake_fpga_mgr_get_rcfg_count(base_ctx->mgr_ctx)); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(base_ctx->bridge_ctx)= ); + KUNIT_EXPECT_EQ(test, 0, fake_fpga_bridge_get_cycles_count(base_ctx->brid= ge_ctx)); + + return 0; +} + +static void fpga_test_exit(struct kunit *test) +{ + struct fpga_base_ctx *base_ctx; + + base_ctx =3D test->priv; + + if (!base_ctx) + return; + + if (base_ctx->region_ctx) + fake_fpga_region_unregister(base_ctx->region_ctx); + + if (base_ctx->bridge_ctx) + fake_fpga_bridge_unregister(base_ctx->bridge_ctx); + + if (base_ctx->mgr_ctx) + fake_fpga_mgr_unregister(base_ctx->mgr_ctx); +} + +static void fpga_test_static_cfg(struct kunit *test) +{ + struct fpga_base_ctx *base_ctx; + struct fpga_image_info *buf_img_info; + struct fpga_image_info *sgt_img_info; + int ret; + + base_ctx =3D test->priv; + + buf_img_info =3D buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATI= C_IMG_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf_img_info); + + /* Configure the FPGA using the image in a buffer */ + base_ctx->region_ctx->region->info =3D buf_img_info; + ret =3D fake_fpga_region_program(base_ctx->region_ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_buf(base_ctx->mgr_ctx); + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_mgr_get_rcfg_count(base_ctx->mgr_ctx)); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(base_ctx->bridge_ctx)= ); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_cycles_count(base_ctx->brid= ge_ctx)); + + kunit_info(test, "FPGA configuration completed using a buffer image\n"); + + sgt_img_info =3D sgt_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATI= C_IMG_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt_img_info); + + /* Re-configure the FPGA using the image in a scatter list */ + base_ctx->region_ctx->region->info =3D sgt_img_info; + ret =3D fake_fpga_region_program(base_ctx->region_ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_sgt(base_ctx->mgr_ctx); + + KUNIT_EXPECT_EQ(test, 2, fake_fpga_mgr_get_rcfg_count(base_ctx->mgr_ctx)); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(base_ctx->bridge_ctx)= ); + KUNIT_EXPECT_EQ(test, 2, fake_fpga_bridge_get_cycles_count(base_ctx->brid= ge_ctx)); + + kunit_info(test, "FPGA configuration completed using scatter gather table= image\n"); + + img_free(sgt_img_info); +} + +static void fpga_test_partial_rcfg(struct kunit *test) +{ + struct fpga_base_ctx *base_ctx; + struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx; + struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx; + struct fpga_image_info *partial_img_info; + int ret; + + base_ctx =3D test->priv; + + /* + * Add two reconfigurable sub-regions, each controlled by a bridge. The + * reconfigurable sub-region are children of their bridges which are, + * in turn, children of the base region. For simplicity, the same image + * is used to configure reconfigurable regions + */ + sub_bridge_0_ctx =3D fake_fpga_bridge_register(&base_ctx->region_ctx->reg= ion->dev, + test); + KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx)); + + sub_region_0_ctx =3D fake_fpga_region_register(base_ctx->mgr_ctx->mgr, + &sub_bridge_0_ctx->bridge->dev, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx)); + + ret =3D fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->b= ridge); + KUNIT_ASSERT_EQ(test, ret, 0); + + sub_bridge_1_ctx =3D fake_fpga_bridge_register(&base_ctx->region_ctx->reg= ion->dev, + test); + KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx)); + + sub_region_1_ctx =3D fake_fpga_region_register(base_ctx->mgr_ctx->mgr, + &sub_bridge_1_ctx->bridge->dev, test); + KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx)); + + ret =3D fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->b= ridge); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Allocate a partial image using a buffer */ + partial_img_info =3D buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, + PARTIAL_IMG_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, partial_img_info); + partial_img_info->flags =3D FPGA_MGR_PARTIAL_RECONFIG; + + /* Re-configure sub-region 0 with the partial image */ + sub_region_0_ctx->region->info =3D partial_img_info; + ret =3D fake_fpga_region_program(sub_region_0_ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_buf(base_ctx->mgr_ctx); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_mgr_get_rcfg_count(base_ctx->mgr_ctx)); + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(sub_bridge_0_ctx)); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_cycles_count(sub_bridge_0_c= tx)); + + /* Re-configure sub-region 1 with the partial image */ + sub_region_1_ctx->region->info =3D partial_img_info; + ret =3D fake_fpga_region_program(sub_region_1_ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + fake_fpga_mgr_check_write_buf(base_ctx->mgr_ctx); + KUNIT_EXPECT_EQ(test, 2, fake_fpga_mgr_get_rcfg_count(base_ctx->mgr_ctx)); + + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(sub_bridge_1_ctx)); + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_cycles_count(sub_bridge_1_c= tx)); + + /* Check that the base bridge has not been disabled during reconfiguratio= n */ + KUNIT_EXPECT_EQ(test, 1, fake_fpga_bridge_get_state(base_ctx->bridge_ctx)= ); + KUNIT_EXPECT_EQ(test, 0, fake_fpga_bridge_get_cycles_count(base_ctx->brid= ge_ctx)); + + img_free(partial_img_info); + fake_fpga_region_unregister(sub_region_0_ctx); + fake_fpga_bridge_unregister(sub_bridge_0_ctx); + fake_fpga_region_unregister(sub_region_1_ctx); + fake_fpga_bridge_unregister(sub_bridge_1_ctx); +} + +static struct kunit_case fpga_test_cases[] =3D { + KUNIT_CASE(fpga_test_static_cfg), + KUNIT_CASE(fpga_test_partial_rcfg), + {} +}; + +static struct kunit_suite fpga_suite =3D { + .name =3D "fpga", + .init =3D fpga_test_init, + .exit =3D fpga_test_exit, + .test_cases =3D fpga_test_cases, +}; + +kunit_test_suites(&fpga_mgr_suite, &fpga_bridge_suite, &fpga_suite); + +MODULE_LICENSE("GPL v2"); --=20 2.39.2