来自 linux 内核驱动程序的 PCI-ISA 桥接通信

PCI-ISA bridge communication from linux kernel driver

我有一个自定义 linux 内核驱动程序,它与旧 ISA 卡(来自具有真正 ISA 总线的旧单处理器 pc)通信。我正在尝试将此驱动程序移植到配备 PCI-ISA 桥接器的新系统中。 旧驱动程序正在写入 I/O ISA 端口:

request_region(0x0280, 8, "foo"); //0x0280 is a jumper-configured address in ISA card hardware.

//Then lots of:
outw_p(val, 0x0280);
val = inw_p(0x0282);

... (ports in use 0x0280, 0x0282 and 0x0284)

我试过相同的代码,但地址映射似乎不再有效。区域请求不会出错,但我总是从所有 inw_p 读取中得到 65535(而在旧系统中,使用相同代码,卡正在回答有意义的数据)。

我无法在任何地方找到要在此代码中编辑的内容以使其与桥一起工作。 我试过将网桥作为 PCI 设备打开并获取其 I/O 端口地址:

dev = pci_get_device(vid, id, NULL); //Called with hardcoded bridge ids from lspci
result = pci_enable_device(dev);     //dev not null, no errors
result = pci_request_regions(dev, "foo"); //No errors
value = pci_resource_start(dev, bar);  //value is always 0 with any bar value

设备正在工作,因为我可以使用 pci_read_config_word 获取其供应商 ID,但我从任何 BAR 值中得到的总是 0,而且 lspci -vvvv 没有给我 address/region 部分:

04:08.0 ISA bridge: Integrated Technology Express, Inc. IT8888F/G PCI to ISA Bridge with SMB [Golden Gate] (rev 03)
    Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
    Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
    Latency: 0

此外,此桥的 BIOS 中似乎没有可用的配置选项。

在 Internet 上,我只发现了一些关于 PCI - ISA 桥接器的一般信息,所以我想问:与 PCI - ISA 桥接器后面的 ISA 卡成功通信所需的程序是什么 linux内核驱动程序?

好的,经过反复试验,我找到了问题所在。我将分享我的经验,希望有人能发现它有用(我的电脑有一个 HD620-H81 主板和 IT8888F/G PCI-ISA 桥)。

PCI-ISA 桥

我的 PCI-ISA 桥接器(可能还有许多其他桥接器)默认配置为“减法解码”pci 模式,这意味着“如果没有其他 pci 设备声明地址,则桥接器声明它”。 您可以通过 运行ning lspci -tlscpi -vv 检查减法解码模式,并且您应该会看到通向 PCI-ISA 桥的树中的每个 PCIe-PCI/PCI-PCI 桥都已配置在减法解码模式下(ISA 桥本身不会显示为减法,因为从 PCI 的角度来看,ISA 桥只是一个 PCI 设备而不是桥)。

这意味着您没有为网桥分配 BAR,也不需要以任何方式直接与 pci 网桥设备交互。您可以直接访问您需要的绝对地址,网桥将负责正确管理它(如果您对细节感兴趣,互联网上已经有很多信息可以用于减法解码)。

总结一下:不需要对网桥本身或 pci 设备进行任何操作,只需确保在 bios 中没有其他设备(例如串行或并行端口)被分配到您指定的 port/address 范围感兴趣(如果感兴趣,请禁用它或更改其地址)。

I/O 端口

我发现我的 ISA 卡所在的端口比它应该所在的端口高 0x0800 个端口(这对我来说是主要问题)。我不确定为什么,也许我的桥添加了一个固定的偏移量(如果你知道原因可以在下面评论!)。
我为找到正确地址所做的工作是 运行 一个函数,该函数迭代所有端口地址,从 0x0100(它跳过最好不要写入随机数据的第一个区域)到 0xFFFF 和 运行s a每个端口地址的卡片检查例程,直到它找到我期望从卡片中得到的正确答案。

int cardFound = 0;
int i;
for(i = 0x0100; i < 0xFFFF && !cardFound; i++)
{
    if (request_region(i, SIZE) == NULL)
        continue;
    
    //Do card detection with ioport_map, iowrite16/ioread16, etc.
    cardFound = do_custom_card_detection_procedure();
   
    release_region(i, SIZE);
}

if (cardFound)
    printk(KERN_INFO "Card found at port %d\n", i);

这种方法的缺点是你可能会通过在端口随机写入数据来做非常糟糕的事情,所以要小心,如果你真的想在做之前尝试检查 /proc/ioport 是否有错误(我搞砸了渲染了四分之一的屏幕,我不得不通过移除电源线和 CMOS 电池来完全重置电源以使其恢复正常:D)。