Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
228 views
in Technique[技术] by (71.8m points)

PCI-ISA bridge communication from linux kernel driver

I have a custom linux kernel driver that communicates to an old ISA card (from an old single processor pc with a true ISA bus). I am trying to port this driver into a new system equipped with a PCI-ISA bridge. The old driver was writing to I/O ISA ports with:

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)

I've tried the same code but the address mapping seems to not work anymore. Region request does not give errors, but I keep getting always 65535 from all inw_p reads (while in the old system with the same code the card was answering with meaningful data).

I can't find anywhere what to edit in this code to make it work with the bridge. I've tried opening the bridge as a PCI device and getting its I/O port address with:

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

Device is working as I can get its vendor id by using pci_read_config_word, but I get always 0 from any BAR value, and also lspci -vvvv gives me no address/region section:

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

Also, no configuration options seems to be available in BIOS for this bridge.

On the internet i found only a few infos about PCI - ISA bridges in general, so I'm asking: what is the procedure necessary to successfully communicate with an ISA card behind a PCI - ISA bridge from a custom linux kernel driver?

question from:https://stackoverflow.com/questions/65909825/pci-isa-bridge-communication-from-linux-kernel-driver

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, after a lot of trial and error I was able to find the problem. I will share my experience in hope for someone to find it useful (my pc has a HD620-H81 motherboard with IT8888F/G PCI-ISA bridge).

PCI-ISA bridge

My PCI-ISA bridge (and probably many others) is configured by default in "Subtractive Decoding" pci mode, which means "if no other pci device claim an address, then the bridge claims it". You can check for subtractive decoding mode by running lspci -t and lscpi -vv, and you should see that every PCIe-PCI/PCI-PCI bridge in the tree leading to your PCI-ISA bridge is configured in subtractive decode mode (the ISA bridge itself won't appear as subtractive because from a PCI perspective the ISA bridge is just a PCI device and not a bridge).

That means that you don't have a BAR assigned to the bridge, nor you need to interact with the pci bridge device directly in any way. You can just access directly the absolute address you need and the bridge will take care of managing it correctly (a lot of information is already available on the internet for subtractive decoding anyway if you're interested in details).

To sum up: no action is needed regarding the bridge itself or pci devices, just make sure in the bios that no other device, such as serial or parallel ports, gets assigned the port/address range that you are interested into (if it does, either disable it or change its address).

I/O Ports

I found that my ISA card was located at a port which is 0x0800 ports above the one where it should be (and that was the main problem for me). I'm not sure about why, maybe my bridge adds a fixed offset (if you know the reason maybe comment it below!).
What I did to find out the correct address was to run a function that iterates all port addresses from 0x0100 (which skips the first zone where is better not to write random data into) to 0xFFFF and runs a card check routine at each single port address until it finds the correct answer I expected from the card.

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
", i);

The downside of this approach is that you may do really bad things by randomly writing data at ports, so be careful and if you really want to try check /proc/ioport for bad ones before doing it (I messed up rendering for a quarter of the screen and I had to do a complete power reset by removing power cord and CMOS battery to bring it back to normal :D).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...