162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * KUnit test for the FPGA Region 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 Red Hat, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Marco Pagani <marpagan@redhat.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <kunit/test.h> 1162306a36Sopenharmony_ci#include <linux/fpga/fpga-bridge.h> 1262306a36Sopenharmony_ci#include <linux/fpga/fpga-mgr.h> 1362306a36Sopenharmony_ci#include <linux/fpga/fpga-region.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct mgr_stats { 1962306a36Sopenharmony_ci u32 write_count; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct bridge_stats { 2362306a36Sopenharmony_ci bool enable; 2462306a36Sopenharmony_ci u32 cycles_count; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct test_ctx { 2862306a36Sopenharmony_ci struct fpga_manager *mgr; 2962306a36Sopenharmony_ci struct platform_device *mgr_pdev; 3062306a36Sopenharmony_ci struct fpga_bridge *bridge; 3162306a36Sopenharmony_ci struct platform_device *bridge_pdev; 3262306a36Sopenharmony_ci struct fpga_region *region; 3362306a36Sopenharmony_ci struct platform_device *region_pdev; 3462306a36Sopenharmony_ci struct bridge_stats bridge_stats; 3562306a36Sopenharmony_ci struct mgr_stats mgr_stats; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int op_write(struct fpga_manager *mgr, const char *buf, size_t count) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci stats->write_count++; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Fake FPGA manager that implements only the write op to count the number 4962306a36Sopenharmony_ci * of programming cycles. The internals of the programming sequence are 5062306a36Sopenharmony_ci * tested in the Manager suite since they are outside the responsibility 5162306a36Sopenharmony_ci * of the Region. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic const struct fpga_manager_ops fake_mgr_ops = { 5462306a36Sopenharmony_ci .write = op_write, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int op_enable_set(struct fpga_bridge *bridge, bool enable) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct bridge_stats *stats = bridge->priv; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (!stats->enable && enable) 6262306a36Sopenharmony_ci stats->cycles_count++; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci stats->enable = enable; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Fake FPGA bridge that implements only enable_set op to count the number 7162306a36Sopenharmony_ci * of activation cycles. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic const struct fpga_bridge_ops fake_bridge_ops = { 7462306a36Sopenharmony_ci .enable_set = op_enable_set, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int fake_region_get_bridges(struct fpga_region *region) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct fpga_bridge *bridge = region->priv; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return fpga_bridge_get_to_list(bridge->dev.parent, region->info, ®ion->bridge_list); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int fake_region_match(struct device *dev, const void *data) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return dev->parent == data; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void fpga_region_test_class_find(struct kunit *test) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct test_ctx *ctx = test->priv; 9262306a36Sopenharmony_ci struct fpga_region *region; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci region = fpga_region_class_find(NULL, &ctx->region_pdev->dev, fake_region_match); 9562306a36Sopenharmony_ci KUNIT_EXPECT_PTR_EQ(test, region, ctx->region); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci put_device(®ion->dev); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * FPGA Region programming test. The Region must call get_bridges() to get 10262306a36Sopenharmony_ci * and control the bridges, and then the Manager for the actual programming. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic void fpga_region_test_program_fpga(struct kunit *test) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct test_ctx *ctx = test->priv; 10762306a36Sopenharmony_ci struct fpga_image_info *img_info; 10862306a36Sopenharmony_ci char img_buf[4]; 10962306a36Sopenharmony_ci int ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci img_info = fpga_image_info_alloc(&ctx->mgr_pdev->dev); 11262306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci img_info->buf = img_buf; 11562306a36Sopenharmony_ci img_info->count = sizeof(img_buf); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ctx->region->info = img_info; 11862306a36Sopenharmony_ci ret = fpga_region_program_fpga(ctx->region); 11962306a36Sopenharmony_ci KUNIT_ASSERT_EQ(test, ret, 0); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 1, ctx->mgr_stats.write_count); 12262306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 1, ctx->bridge_stats.cycles_count); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci fpga_bridges_put(&ctx->region->bridge_list); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = fpga_region_program_fpga(ctx->region); 12762306a36Sopenharmony_ci KUNIT_ASSERT_EQ(test, ret, 0); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, ctx->mgr_stats.write_count); 13062306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, ctx->bridge_stats.cycles_count); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci fpga_bridges_put(&ctx->region->bridge_list); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci fpga_image_info_free(img_info); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * The configuration used in this test suite uses a single bridge to 13962306a36Sopenharmony_ci * limit the code under test to a single unit. The functions used by the 14062306a36Sopenharmony_ci * Region for getting and controlling bridges are tested (with a list of 14162306a36Sopenharmony_ci * multiple bridges) in the Bridge suite. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistatic int fpga_region_test_init(struct kunit *test) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct test_ctx *ctx; 14662306a36Sopenharmony_ci struct fpga_region_info region_info = { 0 }; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); 14962306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ctx->mgr_pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0); 15262306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_pdev); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ctx->mgr = devm_fpga_mgr_register(&ctx->mgr_pdev->dev, "Fake FPGA Manager", &fake_mgr_ops, 15562306a36Sopenharmony_ci &ctx->mgr_stats); 15662306a36Sopenharmony_ci KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr)); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ctx->bridge_pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO, 15962306a36Sopenharmony_ci NULL, 0); 16062306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_pdev); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ctx->bridge = fpga_bridge_register(&ctx->bridge_pdev->dev, "Fake FPGA Bridge", 16362306a36Sopenharmony_ci &fake_bridge_ops, &ctx->bridge_stats); 16462306a36Sopenharmony_ci KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ctx->bridge_stats.enable = true; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ctx->region_pdev = platform_device_register_simple("region_pdev", PLATFORM_DEVID_AUTO, 16962306a36Sopenharmony_ci NULL, 0); 17062306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_pdev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci region_info.mgr = ctx->mgr; 17362306a36Sopenharmony_ci region_info.priv = ctx->bridge; 17462306a36Sopenharmony_ci region_info.get_bridges = fake_region_get_bridges; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ctx->region = fpga_region_register_full(&ctx->region_pdev->dev, ®ion_info); 17762306a36Sopenharmony_ci KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci test->priv = ctx; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void fpga_region_test_exit(struct kunit *test) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct test_ctx *ctx = test->priv; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci fpga_region_unregister(ctx->region); 18962306a36Sopenharmony_ci platform_device_unregister(ctx->region_pdev); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci fpga_bridge_unregister(ctx->bridge); 19262306a36Sopenharmony_ci platform_device_unregister(ctx->bridge_pdev); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci platform_device_unregister(ctx->mgr_pdev); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct kunit_case fpga_region_test_cases[] = { 19862306a36Sopenharmony_ci KUNIT_CASE(fpga_region_test_class_find), 19962306a36Sopenharmony_ci KUNIT_CASE(fpga_region_test_program_fpga), 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci {} 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct kunit_suite fpga_region_suite = { 20562306a36Sopenharmony_ci .name = "fpga_mgr", 20662306a36Sopenharmony_ci .init = fpga_region_test_init, 20762306a36Sopenharmony_ci .exit = fpga_region_test_exit, 20862306a36Sopenharmony_ci .test_cases = fpga_region_test_cases, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cikunit_test_suite(fpga_region_suite); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 214