Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "PCI"
Steven Solie (talk | contribs) |
Steven Solie (talk | contribs) |
||
(One intermediate revision by the same user not shown) | |||
Line 15: | Line 15: | ||
= Obtaining Base Address Registers (BARs) = |
= 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 |
+ | 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 |
+ | 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. |
+ | |||
+ | == Correct Method == |
||
Here is the correct way to obtain a BAR in a device driver: |
Here is the correct way to obtain a BAR in a device driver: |
||
Line 27: | Line 29: | ||
pci_dev->FreeResourceRange(range); |
pci_dev->FreeResourceRange(range); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
+ | |||
+ | == Incorrect Method == |
||
The wrong way looks something like this: |
The wrong way looks something like this: |
||
<syntaxhighlight> |
<syntaxhighlight> |
||
+ | struct PCIDevice *pci_dev = IPCI->FindDevice(...); |
||
+ | |||
struct PCIResourceRange *range = pci_dev->GetResourceRange(0); |
struct PCIResourceRange *range = pci_dev->GetResourceRange(0); |
||
UINT32 base = range->BaseAddress & PCI_BASE_ADDRESS_IO_MASK; |
UINT32 base = range->BaseAddress & PCI_BASE_ADDRESS_IO_MASK; |
||
Line 35: | Line 41: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
− | Note the use of |
+ | 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. |
== Broken PCI_BASE_ADDRESS_IO_MASK == |
== Broken PCI_BASE_ADDRESS_IO_MASK == |
Latest revision as of 22:24, 1 October 2017
Contents
Introduction
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.
Device Access
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.
Correct Method
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);
Incorrect Method
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.
Broken PCI_BASE_ADDRESS_IO_MASK
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.