- 论坛徽章:
- 0
|
原帖由 Cyberman.Wu 于 2009-2-20 14:59 发表
今天看Linux内核中自带的e1000网卡驱动,发现它在probe中有如下操作:
if ((err = pci_request_regions(pdev, e1000_driver_name)))
return err;
但看代码好像这里主要是把BAR0~BAR5分配到的 ...
Essential Linux Device Drivers 这本书对这个函数有讲解,而且我觉得讲得也很不错。我把那一章都拷贝出来:
Accessing PCI Regions
PCI devices contain three addressable regions: configuration space, I/O ports, and device memory. Let's learn how to access these memory regions from a device driver.
Configuration Space
The kernel offers a set of six functions that your driver can use to operate on PCI configuration space:
pci_read_config_[byte|word|dword](struct pci_dev *pdev,
int offset, int *value);
and
pci_write_config_[byte|word|dword](struct pci_dev *pdev,
int offset, int value);
In the argument list, struct pci_dev is the PCI device structure, and offset is the byte position in the configuration space that you want to access. For read functions, value is a pointer to a supplied data buffer, and for write routines, it contains the data to be written.
Let's consider some examples:
To decipher the IRQ number assigned to a card function, use the following:
unsigned char irq;
pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq);
As per the PCI specification, offset 60 inside the PCI configuration space holds the IRQ number assigned to the card. All configuration register offsets are expressively defined in include/linux/pci_regs.h, so use PCI_INTERRUPT_LINE rather than 60 to specify this offset. Similarly, to read the PCI status register (two bytes at offset six in the configuration space), do this:
unsigned short status;
pci_read_config_word(pdev, PCI_STATUS, &status);
Only the first 64 bytes of the configuration space are standardized. The device manufacturer defines desired semantics to the rest. The Xircom card used earlier, assigns four bytes at offset 64 for power management purposes. To disable power management, the Xircom CardBus driver, drivers/net/tulip/xircom_cb.c, does this:
#define PCI_POWERMGMT 0x40
pci_write_config_dword(pdev, PCI_POWERMGMT, 0x0000);
I/O and Memory
PCI cards have up to six I/O or memory regions. I/O regions contain registers, and memory regions hold data. Video cards, for example, have I/O spaces that accommodate control registers and memory regions that map to frame buffers. Not all cards have addressable memory regions, however. The semantics of I/O and memory spaces are hardware-dependent and can be obtained from the device data sheet.
Like for configuration memory, the kernel offers a set of helpers to operate on I/O and memory regions of PCI devices:
Code View:
unsigned long pci_resource_[start|len|end|flags] (struct pci_dev *pdev, int bar);
To operate on an I/O region such as the device control registers of a PCI video card, the driver needs to do the following:
1. Get the I/O base address from the appropriate base address register (bar) in the configuration space:
unsigned long io_base = pci_resource_start(pdev, bar);
This assumes that the device control registers for this card are mapped to the memory region associated with bar, whose value can range from 0 through 5, as shown in Table 10.2.
2. Mark this region as being spoken for, using the kernel's request_region() regulatory mechanism discussed in Chapter 5, "Character Drivers":
request_region(io_base, length, "my_driver");
Here, length is the size of the control register space and my_driver identifies the region's owner. Look for the entry containing my_driver in /proc/ioports to spot this memory region.
You may instead use the wrapper function pci_request_region(), defined in drivers/pci/pci.c.
3. Add the register's offset obtained from the data-sheet, to the base address gleaned in Step 1. Operate on this address using the inb() and outb() family of functions discussed in Chapter 5:
/* Read */
register_data = inl(io_base + REGISTER_OFFSET);
/* Use */
/* ... */
/* Write */
outl(register_data, iobase + REGISTER_OFFSET);
To operate on a memory region such as the frame buffer on the above PCI video card, follow these steps:
1. Get the base address, length, and flags associated with the memory region:
unsigned long mmio_base = pci_resource_start(pdev, bar);
unsigned long mmio_length = pci_resource_length(pdev, bar);
unsigned long mmio_flags = pci_resource_flags(pdev, bar);
This assumes that this memory is mapped to the base address register, bar.
2. Mark ownership of this region using the kernel's request_mem_region() regulatory mechanism:
request_mem_region(mmio_base, mmio_length, "my_driver");
You may instead use the wrapper function pci_request_region(), mentioned previously.
3. Obtain CPU access to the device memory obtained in Step 1. Certain memory regions, such as the ones that hold registers, need to guard against side effects, so they are marked as not being prefetchable (or cacheable) by the CPU. Other regions, such as the one used in this example, can be cached. Depending on the access flag, use the appropriate function to obtain kernel virtual addresses corresponding to the mapped region:
void __iomem *buffer;
if (flags & IORESOURCE_CACHEABLE) {
buffer = ioremap(mmio_base, mmio_length);
} else {
buffer = ioremap_nocache(mmio_base, mmio_length);
}
To be safe, and to avoid performing the preceding checks, use the services of pci_iomap() defined in lib/iomap.c instead:
buffer = pci_iomap(pdev, bar, mmio_length); |
|