Embedded 版 (精华区)
发信人: Zinux (Linux技工), 信区: Embedded_system
标 题: PCI Management in Linux 2.2
发信站: 哈工大紫丁香 (2001年10月26日18:25:40 星期五), 站内信件
Linux Magazine (http://www.linux-mag.com) March 2000
Copyright Linux Magazine ?2000
GEARHEADS
PCI Management in Linux 2.2
by Alan Cox
Peripheral Component Interconnect (PCI) is a widely used bus standard
that provides several advantages over other bus standards, such as
EISA. Standard on most Pentium motherboards, PCI is a fast, wide
(32-bit and 64-bit), and processor-independent bus. When PCI support
was first added to Linux, the kernel interface was a wrapper to the
PCI BIOS32 functions. There were several problems with this approach:
* PCI BIOS is found only on PC-class machines.
* PCI BIOS is found only on PC-class machines.
* The PCI BIOS itself represents only certain constructs, and there
are non-PC machines that have PCI setups that cannot be described by
the PCI BIOS.
* One or two machines have 32-bit PCI BIOS functions that don't quite
do what they are supposed to.
Linux 2.2 provides a generic PCI interface. The Linux x86 kernel
actually tries to drive the hardware directly, but if it finds
something it doesn't understand, it may call PC BIOS32 functions.
Drivers can continue to use the old PCI interface but will need
updating for future kernels. If the driver is intended to work cross
platform, then it really should be updated.
Most of the functions map very simply from the old format to the new
one. The PCI BIOS is based on the idea of bus, device, and function
numbers. The new code uses pci_ bus and pci_dev structures. The first
new PCI function is:
pci_present()
This function checks whether one or more PCI buses were found on the
machine. The older kernels had a pcibios_present() function, whose
usage is identical.
Having confirmed that PCI is actually present, you can scan the PCI
buses for devices. PCI devices are identified by several configuration
bytes. The main pair of these are the vendor and device identity.
Vendors are issued a unique identity and are supposed to issue unique
device identifiers for the boards that they make. Another advantage of
PCI is that it provides version and programming-interface information
so that board variants can be discovered.
In Linux 2.2 a scan for a PCI device normally uses the pci_
find_device() function. A typical example would be:
struct pci_dev *pdev=NULL;
while((pdev=pci_find_device(PCI_MY_VENDOR,
PCI_MY_DEVICE, pdev))!=NULL)
{
/* Found a device */
setup_device(pdev);
}
The pci_find_device takes three arguments. The first is the vendor
identification, the second the board identity, and the third is the
last return from the function, or NULL to indicate you wish to start
scanning from the beginning. This example calls setup_device() for
each instance of the device found.
Another nice thing that PCI does is that it handles all the resource
configuration for you. Normally the PCI BIOS handles this, but on
other platforms, the firmware- or architecture-specific Linux code
will do this. By the time your driver comes to look for cards, they
have been assigned system resources. Not only have they been assigned,
have been assigned system resources. Not only have they been assigned,
but they can be retrieved without the user having to provide the
information.
Linux provides the core information in the pci_dev structure. It also
allows the per-card PCI configuration space to be read and written.
Care should be taken whenever you are looking at resource data
directly. On many systems, the values configured on the card will not
match the values that the kernel provides. This is because many non-PC
machines have multiple PCI buses, and the PCI buses are mapped into
the system in ways that the cards on the bus cannot see.
Linux provides the IRQ and the PCI BARs (Base Address Registers)
directly. You should always use the copies directly provided to avoid
unpleasant non-x86 surprises. Listing One shows a setup_device()
function.
Listing One: The setup_device () Function
void setup_device(struct pci_dev *dev)
{
int io_addr = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
int irq = dev->irq;
int irq = dev->irq;
u8 rev;
pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
if(rev<64)
printk("Found a WonderWidget 500 at I/O 0x%04X, IRQ
%d.\n", io_addr, irq);
else
printk("Found a WonderWidget 600 at I/O 0x%04X, IRQ
%d.\n", io_addr, irq);
/* Check for a common BIOS problem - if you expect an IRQ you
might not get it */
if(irq==0)
{
printk(KERN_ERR "BIOS has not assigned the WonderWidget an
interrupt.\n");
return;
}
/* Now do the board initialization knowing the resources */
init_device(io_addr, irq, rev<64?0:1);
init_device(io_addr, irq, rev<64?0:1);
pci_set_master(dev);
}
When your card is configured by the BIOS, certain features can be left
disabled. In particular, most BIOSs leave the "master" bit cleared.
This prevents boards from copying data into main memory of their own
volition, for example if they don't notice a reboot. If is the job of
the bus-mastering drivers to enable this. Linux 2.2 provides a handy
helper function:
pci_set_master(struct pci_dev *)
This will check if the flag needs setting, and, if so, set it and
provide a convenient kernel message for the user.
The example setup_device function also uses pci_read_ config_byte to
read data from the configuration space. The kernel provides a full set
read data from the configuration space. The kernel provides a full set
of functions relating to the configuration space. pci_read_
config_byte,pci_read_config_word, and pci_read_config_dword retrieve
8, 16, or 32 bits respectively from the configuration space.
pci_write_config_byte, pci_write_config_word, and
pci_write_config_dword write 8, 16, and 32 bits to the configuration
space. The PCI configuration space is separate to both the I/O and
memory resources on a PC and thus can be accessed only via these
functions.
The final set of PCI functions that are sometimes useful scan the PCI
bus in different ways. pci_find_class looks for devices that match a
given class. The PCI specification categorizes devices into classes
and allows you to scan by class for devices. Some specifications such
as those shared by many vendors specify only a PCI class and maybe a
programming revision. To find UHCI-specification USB controllers, for
example, the kernel uses
struct pci_dev *pdev = NULL;
while((pdev=pci_find_class
(PCI_CLASS_SERIAL_USB <<8, pdev))!=NULL)
(PCI_CLASS_SERIAL_USB <<8, pdev))!=NULL)
{
u8 type;
pci_read_config_byte(dev,
PCI_CLASS_PROG, &type);
if(type!=0)
continue;
/* FOUND IT */
}
Another example of a shared interface that uses this is I2O. In these
cases, the vendor identification is useful only for reporting the
actual card type and occasionally for working around board-specific
bugs.
The final way to scan for a PCI device is to use pci_ find_slot, which
lets you scan the PCI slots and functions in a specific order. It is
rarely useful but can be handy if you need to control the order of the
PCI scan for a specific type of device. This normally occurs when you
need to duplicate the order in which the on-board BIOS reports the
cards, or when you want to force Linux and non-Linux drivers to report
the controllers in the same order. pci_find_ slot() is passed the bus
the controllers in the same order. pci_find_ slot() is passed the bus
number and the device-function value (slot<<3 | function).
PCI Interrupts and Other Perils
An important PCI-bus concept not normally found in ISA bus devices is
shared interrupt handling. PCI-bus interrupts are also
level-triggered, which means that an interrupt is constantly indicated
until the card decides to clear it. This combination of features puts
some important constraints on the way the driver handles interrupts.
The driver should always register PCI interrupts with the SA_SHIRQ
flag in order to indicate that the interrupt line can be shared. If it
fails to do this, other PCI devices on the system may not be able to
function, and the user will probably get peeved.
Since the interrupt is shared, both the PCI driver and the kernel need
ways to talk about each handler for the interrupt. You must register
interrupts for shared devices with a non-NULL dev_id; otherwise the
kernel cannot tell different interrupt handlers apart when you need to
kernel cannot tell different interrupt handlers apart when you need to
free one with free_irq(). The dev_id is passed to the interrupt
handler, so it is very useful anyway. For example, you might want to
do:
if(request_irq(dev->irq, dev_interrupt,
SA_SHIRQ, "wonderwidget",
dev))
return -EAGAIN;
and when finished, use
free_irq(dev->irq, dev)
to ensure that the correct interrupt using this IRQ line is freed.
This interrupt handler is invoked with the dev field that was passed.
This makes life very simple. Instead of searching for devices using
this interrupt, you can do something like Listing Two.
Listing Two: Using the dev_id
Listing Two: Using the dev_id
static void dev_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct wonderwidget *dev = dev_id;
u32 status;
/* It is important to exit interrupt handlers that are not for us
as fast as possible */
if((status=inl(dev->port))==0) /* Not our interrupt */
return;
if(status&1)
handle_rx_intr(dev);
....
}
The final thing to understand about PCI interrupts being shared is
that you must be very careful that you always handle the interrupt.
You also need to be sure you never generate an interrupt before you
You also need to be sure you never generate an interrupt before you
install a handler. Because the PCI bus is level-triggered, if you
cause an interrupt you cannot handle, you will hang the machine. This
means you should be particularly careful when writing initialization
code that you register the interrupt handler before you enable
interrupts on the device. Similarly, at shutdown, you need to be
careful to disable interrupts on the card before you unregister the
interrupt handler.
Supporting PCI bus on Linux requires the use of a few more functions
than ISA bus, and a little care in the interrupt handling. In return,
you get all the configuration without user intervention.
--
puke!
技工而已
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.239.152]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:206.578毫秒