MEN 变色龙总线

简介

本文档描述了 MEN 变色龙总线(在本文档中称为 MCB)的架构和实现。

本文档的范围

本文档旨在简要概述当前的实现,绝不描述基于 MCB 的设备的完整可能性。

当前实现的限制

当前的实现仅限于使用单个内存资源并共享 PCI 传统 IRQ 的基于 PCI 和 PCIe 的载体设备。 未实现的是

  • 多资源 MCB 设备,如 VME 控制器或 M-Module 载体。

  • 需要另一个 MCB 设备的 MCB 设备,例如用于 DMA 控制器缓冲区描述符的 SRAM 或视频控制器的视频内存。

  • 每个载体设备的 IRQ 域,对于每个 MCB 设备有一个(或多个)IRQ 的载体设备,例如具有 MSI 或 MSI-X 支持的基于 PCIe 的载体。

架构

MCB 分为 3 个功能块

  • MEN 变色龙总线本身,

  • MCB 载体设备的驱动程序,以及

  • 变色龙表的解析器。

MEN 变色龙总线

MEN 变色龙总线是一个人为的总线系统,它连接到 MEN Mikro Elektronik GmbH 生产的某些硬件上发现的所谓变色龙 FPGA 设备。这些设备是在单个 FPGA 中实现的多功能设备,通常通过某种 PCI 或 PCIe 链接连接。每个 FPGA 都包含一个描述 FPGA 内容的标头部分。标头列出了设备 ID、PCI BAR、距 PCI BAR 起始的偏移量、FPGA 中的大小、中断号以及一些其他当前 MCB 实现未处理的属性。

载体设备

载体设备只是变色龙 FPGA 所连接的真实物理总线的抽象。一些 IP 核驱动程序可能需要与载体设备的属性进行交互(例如查询 PCI 设备的 IRQ 号)。为了提供与真实硬件总线的抽象,MCB 载体设备提供了回调方法,将驱动程序的 MCB 函数调用转换为硬件相关函数调用。例如,载体设备可以实现 get_irq() 方法,该方法可以转换为硬件总线查询以获取设备应使用的 IRQ 号。

解析器

解析器读取变色龙设备的前 512 个字节并解析变色龙表。目前,解析器仅支持变色龙表的变色龙 v2 变体,但可以轻松地进行调整以支持较旧的或可能的未来变体。在解析表的条目时,会分配新的 MCB 设备,并根据变色龙表中的资源分配为其分配资源。资源分配完成后,MCB 设备将在 MCB 处以及 Linux 内核的驱动程序核心处注册。

资源处理

当前的实现为每个 MCB 设备分配一个内存和一个 IRQ 资源。但这将来可能会发生变化。

内存资源

每个 MCB 设备都只有一个内存资源,可以从 MCB 总线请求。此内存资源是 MCB 设备在载体内的物理地址,旨在传递给 ioremap() 及其相关函数。 它已经通过调用 request_mem_region() 从内核请求。

IRQ

每个 MCB 设备都只有一个 IRQ 资源,可以从 MCB 总线请求。如果载体设备驱动程序实现了 ->get_irq() 回调方法,则将返回载体设备分配的 IRQ 号,否则将返回变色龙表中的 IRQ 号。此号码适合传递给 request_irq()

编写 MCB 驱动程序

驱动程序结构

每个 MCB 驱动程序都有一个结构来标识设备驱动程序以及标识 FPGA 内部 IP 核的设备 ID。驱动程序结构还包含回调方法,这些方法在驱动程序探测和从系统中删除时执行

static const struct mcb_device_id foo_ids[] = {
        { .device = 0x123 },
        { }
};
MODULE_DEVICE_TABLE(mcb, foo_ids);

static struct mcb_driver foo_driver = {
driver = {
        .name = "foo-bar",
        .owner = THIS_MODULE,
},
        .probe = foo_probe,
        .remove = foo_remove,
        .id_table = foo_ids,
};

探测和连接

加载驱动程序并找到其服务的 MCB 设备时,MCB 核心将调用驱动程序的探测回调方法。当驱动程序从系统中删除时,MCB 核心将调用驱动程序的删除回调方法

static init foo_probe(struct mcb_device *mdev, const struct mcb_device_id *id);
static void foo_remove(struct mcb_device *mdev);

初始化驱动程序

当内核启动或您的 foo 驱动程序模块插入时,您必须执行驱动程序初始化。通常,在 MCB 核心注册您的驱动程序模块就足够了

static int __init foo_init(void)
{
        return mcb_register_driver(&foo_driver);
}
module_init(foo_init);

static void __exit foo_exit(void)
{
        mcb_unregister_driver(&foo_driver);
}
module_exit(foo_exit);

module_mcb_driver() 宏可用于减少上述代码

module_mcb_driver(foo_driver);

使用 DMA

要使用内核的 DMA-API 函数,您需要使用载体设备的“struct device”。幸运的是,“struct mcb_device”嵌入了一个指向载体设备的指针 (->dma_dev),用于 DMA 目的

ret = dma_set_mask_and_coherent(&mdev->dma_dev, DMA_BIT_MASK(dma_bits));
if (rc)
        /* Handle errors */