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 Core 驱动程序可能需要与载体设备的属性进行交互(例如,查询 PCI 设备的 IRQ 号)。为了提供与真实硬件总线的抽象,MCB 载体设备提供了回调方法,用于将驱动程序的 MCB 函数调用转换为与硬件相关的函数调用。例如,载体设备可以实现 get_irq() 方法,该方法可以转换为硬件总线查询,以获取设备应使用的 IRQ 号。
解析器¶
解析器读取变色龙设备的前 512 个字节并解析变色龙表。当前,解析器仅支持变色龙表的 Chameleon 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 Core 的设备 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 */