Copyright (c) 2012-2016 Hyperion Entertainment and contributors.
Many Amiga computers utilitize the PCI (Peripheral Component Interconnect) local bus standard. Many more support the PCI Express (Peripheral Component Interconnect Express) local bus standard. No matther which flavour of PCI is in use, the pci interface is the way to utilize them.
The Interface and its Functions
Expansion Library contains the "pci" interface. Therefore, you must first open Expansion Library and then obtain the pci interface in order to access the methods. The "pci" interface has methods to find a specific device or allocate resource ranges for a device that has not been configured yet.
Device addressing is done entirely through an abstract interface. There is no bus/device/function number. Instead, a device is represented by means of a library-exported interface of type "pci_device", represented by an interface structure PCIDevice.
You access a PCI device by first finding it with the FindDevice() function. Once a device has been located a device driver must also lock the device for as long as it is in use.
Obtaining Base Address Registers (BARs)
Each PCI device has a set of up to 6 BARs (Base Address Registers). These can be accessed either by reading from config space, which would involve additional calculations on the user's side, or by using the GetResourceRange() method of the pci_device. GetResourceRange() returns a read-only pointer to a read-only structure containing information about a specific BAR, including its classification into memory or I/O space, prefetchability, etc. The pointer must be freed with FreeResourceRange() when you are done accessing it.
A common error has crept into many AmigaOS device drivers when using the GetResourceRange() method. The error is rather subtle and only makes a difference when the Amiga hardware uses different addresses for the bus and CPU access. In essence, two PCI devices may have the same address in PCI space but unique addresses in CPU address space.
Here is the correct way to obtain a BAR in a device driver:
struct PCIDevice *pci_dev = IPCI->FindDevice(...); struct PCIResourceRange *range = pci_dev->GetResourceRange(0); UINT32 base = range->BaseAddress; pci_dev->FreeResourceRange(range);
The wrong way looks something like this:
struct PCIDevice *pci_dev = IPCI->FindDevice(...); struct PCIResourceRange *range = pci_dev->GetResourceRange(0); UINT32 base = range->BaseAddress & PCI_BASE_ADDRESS_IO_MASK; pci_dev->FreeResourceRange(range);
Note the use of an address mask in the incorrect method. This is wrong because it assumes the PCI bus address is suitable for CPU access.
An additional error crept into the PCI_BASE_ADDRESS_IO_MASK in the expansion/pci.h header file. The correct value for this mask is 0xFFFFFFFE. Keep in mind this address mask only applies to the PCI device address space. Therefore, you cannot use this address to access the device from the CPU.