Chapter 15. Overview of Peripheral Buses

While Chapter 8, introduced the lowest levels of hardware control, this chapter provides an overview of the higher-level bus architectures. A bus is made up of both an electrical interface and a programming interface. In this chapter, I’m going to deal with the programming interface.

This chapter covers a number of bus architectures. However, the primary focus is on the kernel functions that access PCI peripherals, because these days the PCI bus is the most commonly used peripheral bus, and the one that is best supported by the kernel.

The PCI Interface

Although many computer users think of PCI (Peripheral Component Interconnect) as a way of laying out electrical wires, it is actually a complete set of specifications defining how different parts of a computer should interact.

The PCI specification covers most issues related to computer interfaces. I’m not going to cover it all here; in this section I’m mainly concerned with how a PCI driver can find its hardware and gain access to it. The probing techniques discussed in Section 2.6 in Chapter 2, and Section 9.2.2 in Chapter 9, can be used with PCI devices, but the specification offers an alternative to probing.

The PCI architecture was designed as a replacement for the ISA standard, with three main goals: to get better performance when transferring data between the computer and its peripherals, to be as platform-independent as possible, and to simplify adding and removing peripherals to the system.

The PCI bus achieves better performance by using a higher clock rate than ISA; its clock runs at 25 or 33 MHz (its actual rate being a sub-multiple of the system clock), and a 66 MHz extension is upcoming. Moreover, it is equipped with a 32-bit data bus, and a 64-bit extension has been included in the specification. Platform independence is often a goal in the design of a computer bus, and it’s an especially important feature of PCI, because the PC world has always been dominated by processor-specific interface standards.

What is most relevant to the driver writer, however, is the support for autodetection of interface boards. PCI devices are jumperless (unlike most ISA peripherals) and are automatically configured at boot time. The device driver, then, must be able to access configuration information in the device in order to complete initialization. This happens without the need to perform any probing.

PCI Addressing

Each peripheral is identified by a bus number, a device number, and a function number. While the PCI specification permits a system to host up to 256 buses, PCs have only one. Each bus hosts up to 32 devices, and each device can be a multifunction board (such as an audio device with an accompanying CD-ROM drive) with a maximum of 8 functions. Each function can be identified by one 16-bit key or two 8-bit keys. The Linux kernel uses the latter approach.

The hardware circuitry of each peripheral board answers queries pertaining to three address spaces: memory locations, I/O ports, and configuration registers. The first two address spaces are shared by all the devices on a PCI bus (i.e., when you access a memory location, all the devices see the bus cycle at the same time). The configuration space, on the other hand, exploits ``geographical addressing;'' each slot has a private enable wire for configuration transactions, and the PCI controller accesses one board at a time with no address collision. As far as the driver is concerned, memory and I/O are accessed in the usual ways via inb, memcpy, etc. Configuration transactions, on the other hand, are performed by calling specific kernel functions to access configuration registers. As far as interrupts are concerned, every PCI device has 4 interrupt pins, whose routing to the processor IRQ lines is the responsibility of the motherboard; PCI interrupts can be shared by design, so that even a processor with a limited number of IRQ lines can host many PCI interface boards.

The I/O space in a PCI bus uses a 32-bit address bus (leading to 4GB of I/O ports), while the memory space can be accessed with either 32-bit or 64-bit addresses. Addresses are supposed to be unique to one device, but it’s possible for two devices to map erroneously to the same address, making it impossible to access either one. The good news is that every memory and I/O address region offered by the interface board can be remapped by means of configuration transactions. This is the mechanism by which the devices can be initialized at boot time to avoid address collisions. The addresses to which these regions are currently mapped can be read from the configuration space, so the Linux driver can access its devices without probing. Once the configuration registers have been read, the driver can safely access its hardware.

The PCI configuration space consists of 256 bytes for each device function, and the layout of the configuration registers is standardized. Four bytes of the configuration space hold a unique function ID, so the driver can identify its device by looking for the specific ID for that peripheral.[38] In summary, each device board is geographically addressed to retrieve its configuration registers; this information can be used to identify the board and take further action.

It should be clear from this description that the main innovation of the PCI interface standard over ISA is the configuration address space. Therefore, in addition to the usual driver code, a PCI driver needs the ability to access configuration space.

For the remainder of this chapter, I’ll use the word ``device'' to refer to a device function, because each function in a multi-function board acts as an independent entity. When I refer to a device I mean the tuple ``bus number, device number, function number.'' As I mentioned earlier, each tuple is represented in Linux by two 8-bit numbers.

Boot Time

Let’s look at how PCI works, starting from system boot, since that’s when the devices are configured.

When power is applied to a PCI device, the hardware shuts down. In other words, the device will only respond to configuration transactions. At power on, the device has no memory and no I/O ports mapped in the computer’s address space; every other device-specific feature, like the interrupt lines, is disabled as well.

Fortunately, every PCI motherboard is equipped with PCI-aware firmware, called the BIOS, NVRAM, or PROM depending on the platform. The firmware offers access to the device configuration address space, even if the processor’s instruction set doesn’t offer such a capability.

At system boot, the firmware performs configuration transactions with every PCI peripheral in order to allocate a safe place for any address region it offers. By the time a device driver accesses the device, its memory and I/O regions have already been mapped into the processor’s address space. The driver can change this default assignment, but it usually doesn’t unless there are device-dependent reasons to do so.

In Linux, the user can look at the PCI device list by reading /proc/pci, which is a text file that has an entry for each PCI board in the system. Here is an example of a /proc/pci entry:

Bus  0, device  13, function  0:
  Multimedia video controller: Intel SAA7116 (rev 0).
    Medium devsel.  IRQ 10.  Master Capable.  Latency=32.
    Non-prefetchable 32 bit memory at 0xf1000000.

Each entry in /proc/pci is a summary of the device-independent features of one device as described by its configuration registers. The entry above, for example, tells us that the device has on-board memory that has been mapped to address 0xf1000000. The meaning of some of the more exotic details will become clear later, after I introduce the configuration registers.

Detecting the Device

As mentioned earlier, the layout of the configuration space is device-independent. In this section, we’ll look at the configuration registers that are used to identify the peripherals.

PCI devices feature a 256-byte address space. The first 64 bytes are standardized, while the rest is device-dependent. Figure 15.1 shows the layout of the device-independent configuration space.

The standardized PCI configuration registers

Figure 15-1. The standardized PCI configuration registers

As the figure shows, some of the PCI configuration registers are required and some are optional. Every PCI device must contain meaningful values in the compulsory registers, while the contents of the optional registers depend on the actual capabilities of the peripheral. The optional fields are not used unless the contents of the compulsory fields indicate that they are valid. Thus, the compulsory fields assert the board’s capabilities, including whether the other fields are usable or not.

It’s interesting to note that the PCI registers are always little-endian. Although the standard is designed to be architecture-independent, the PCI designers sometimes show a slight bias toward the PC environment. The driver writer should be careful about byte ordering when accessing multi-byte configuration registers; code that works on the PC might not work on other platforms. The Linux developers have taken care of the byte-ordering problem (see the next section, Section 15.1.4), but the issue must be kept in mind. Unfortunately, the standard functions ntohs and ntohl can’t be used because the network byte order is the opposite of the PCI order; no standardized functions exist in Linux 2.0 to convert from PCI byte-order to host byte-order, and every driver that builds up multi-byte values from single bytes needs to be careful to deal correctly with endianness. Version 2.1.10 of the kernel introduced a few functions to deal with these byte-order issues; they are introduced in Section 17.7, in Chapter 17.

Describing all the configuration items is beyond the scope of this book. Usually, the technical documentation released with each device describes the supported registers. What we’re interested in is how a driver can look for its device and how it can access the device’s configuration space.

Three PCI registers identify a device: vendor, deviceID, and class. Every PCI peripheral puts its own values in these read-only registers, and the driver can use them to look for the device. Let’s look at these registers in more detail:

vendor

This 16-bit register identifies a hardware manufacturer. For instance, every Intel device is marked with the same vendor number, 8086 hex (just a random value?). There is a global registry of such numbers, and manufacturers must apply to have a unique number assigned.

deviceID

This is another 16-bit register, selected by the manufacturer; no official registration is required for the deviceID. This ID is usually paired with the vendor ID to make a unique 32-bit identifier for a hardware device. I’ll use the word signature to refer to the vendor/deviceID pair. A device driver usually relies on the signature to identify its device; the driver writer knows from the hardware docs what value to look for.

class

Every peripheral device belongs to a class. The class register is a 16-bit value whose top eight bits identify the ``base class'' (or group). For example, ``ethernet'' and ``token ring'' are two classes belonging to the ``network'' group, while the ``serial'' and ``parallel'' classes belong to the ``communication'' group. Some drivers can support several similar devices, each of them featuring a different signature but all belonging to the same class; these drivers can rely on the class register to identify their peripherals, as shown later.

The following headers, macros, and functions should be used by a PCI driver to look for its hardware device:

#include <linux/config.h>

The driver needs to know if the PCI functions are available in the kernel. By including this header, the driver gains access to the CONFIG_ macros, including CONFIG_PCI, which is described below. From 1.3.73 on, this header is included by <linux/fs.h>; you need to include it explicitly if you want to be backward compatible.

CONFIG_PCI

This macro is defined if the kernel includes support for PCI BIOS calls. Not every computer includes a PCI bus, so the kernel developers chose to make PCI support a compile-time option to save memory when running Linux on non-PCI computers. If CONFIG_PCI is not defined, the rest of the functions in this list are not available, and the driver should use a preprocessor conditional to mask out PCI support and avoid ``undefined symbol'' errors at load time.

#include <linux/bios32.h>

This header declares all the prototypes introduced in this section and should always be included. This header also defines symbolic values for the error codes returned by the functions. The header didn’t change between 1.2 and 2.0, so there are no portability issues.

int pcibios_present(void);

Since the PCI-related functions don’t make sense on non-PCI computers, the pcibios_present function tells the driver if the computer supports PCI; it returns a boolean value of true (non-zero) if the BIOS is PCI-aware. Even if CONFIG_PCI is defined, the PCI functionality is a run-time option. Therefore, you need to check pcibios_present to make sure the computer supports PCI before calling the functions introduced below.

#include <linux/pci.h>

This header defines symbolic names for all the numeric values used by the remaining functions. Not every deviceID is listed in this file, but you might want to look here before defining your own macros for id, vendor, and class. Note that this header is constantly getting bigger as new symbolic definitions are added for new devices.

int pcibios_find_device (unsigned short vendor, ,                          unsigned short id, ,                          unsigned short index, ,                          unsigned char *bus, ,                          unsigned char *function);

If CONFIG_PCI is defined and pcibios_present is true, this function is used to request information about the device from the BIOS. The vendor/id pair identifies the device. index is used to support multiple devices with the same vendor/id identifier and is explained below. The call to pcibios_find_device returns the position of the device in the bus and function pointers. The return codes are 0 to indicate success and non-zero for failure.

int pcibios_find_class (unsigned int class_code, ,                         unsigned short index, ,                         unsigned char *bus, ,                         unsigned char *function);

This function is similar to the previous one, but it looks for devices belonging to a specific class. The class_code argument should be passed as the 16-bit class register shifted left by 8 bits, because of the way the BIOS interface uses the class register. Once again, a return value of 0 means success, while non-zero means there was an error.

char *pcibios_strerror(int error);

This function can be used to translate a PCI error code, such as the one returned by pcibios_find_device, into a string. You might want to print an error message if one of the find calls returns neither PCIBIOS_SUCCESSFUL (0) nor PCIBIOS_DEVICE_NOT_FOUND, which is the expected error code after all the devices have been found.

The code below is typical of the code used by a driver at load time to detect its devices. As outlined above, the lookup can be based on either the signature or the device class. In either case, the driver must store the bus and function values, which are used later to identify the device. The first five bits if the function value identify the device and the next three bits identify the function.

In the code below, every device-specific symbol is prefixed with jail_ (just another instruction list), lowercase or uppercase depending on the kind of symbol.

If the driver can rely on a unique vendor/id pair, the following loop can be used to initialize the driver:

#ifdef CONFIG_PCI
    if (pcibios_present()) {
        unsigned char bus, function;
        int index, result;

        for (index=0; index < JAIL_MAX_DEV; index++) {
            result = pcibios_find_device(JAIL_VENDOR, JAIL_ID, index,
                                         &bus, &function);
            if (result != PCIBIOS_SUCCESSFUL)
                break;
            jail_init_dev(bus, function);
        }
        if (result != PCI_BIOS_DEVICE_NOT_FOUND) {
            printk(KERN_WARNING "jail: pci error: %s
",
                   pcibios_strerror(result));
        }
    }
    if (index == 0)
        return -ENODEV;
#else
   return -ENODEV; /* no PCI bios: no devices */
#endif

This code excerpt is correct if the driver deals only with one kind of PCI device, identified by JAIL_VENDOR and JAIL_ID.

However, many drivers are more flexible and can handle both PCI and ISA boards. In that case, the driver probes for the ISA device only if no PCI board is detected or if CONFIG_PCIBIOS is undefined.

It is also common for different vendors to build compatible hardware. The driver should work with all compatible devices even if their signatures are different. To do that, init_module should invoke pcibios_find_class instead of pcibios_find_device.

Using pcibios_find_class requires that jail_init_dev perform a little more work than in the example. The function returns successfully any time it finds a device belonging to the right class, but the driver still has to verify that the signature is one of the supported ones. This task is performed by a series of conditionals that end up discarding any unexpected device.

Some PCI peripherals contain a general-purpose PCI interface chip and device-specific circuitry. Every peripheral board that uses the same interface chip has the same signature and the driver must perform additional probing to be sure it is dealing with the correct peripheral device. Therefore, sometimes a function like jail_init_dev has to be ready to do the device-specific extra checking and to discard a device even though it might have the correct signature.

Accessing the Configuration Space

After the driver has detected the device, it usually needs to read from or write to the three address spaces: memory, port, and configuration. In particular, accessing the configuration space is vital to the driver because it is the only way it can find out where the device is mapped in memory and in the I/O space.

Since the microprocessor has no way to access the configuration space directly, the computer vendor has to provide a way to do it. The exact implementation is therefore vendor-dependent and not relevant to this discussion. Fortunately, the software interface to the transactions described below is standardized, and neither the driver nor the Linux kernel need to be aware of the details.

As far as the driver is concerned, the configuration space can be accessed through 8-bit, 16-bit, or 32-bit data transfers. The relevant functions are prototyped in <linux/bios32.h>:

int pcibios_read_config_byte (unsigned char bus, ,                               unsigned char function, ,                               unsigned char where, ,                               unsigned char *ptr); , int pcibios_read_config_word (unsigned char bus, ,                               unsigned char function, ,                               unsigned char where, ,                               unsigned short *ptr); , int pcibios_read_config_dword (unsigned char bus, ,                                unsigned char function, ,                                unsigned char where, ,                                unsigned int *ptr);

Read 1, 2, or 4 bytes from the configuration space of the device identified by bus and function. The where argument is the byte offset from the beginning of the configuration space. The value fetched from the configuration space is returned through ptr, and the return value of the functions is an error code. The word and dword functions convert the value just read from little-endian to the native byte order of the processor, so you shouldn’t need to deal with byte ordering.

int pcibios_write_config_byte (unsigned char bus, ,                                unsigned char function, ,                                unsigned char where, ,                                unsigned char val); , int pcibios_write_config_word (unsigned char bus, ,                                unsigned char function, ,                                unsigned char where, ,                                unsigned short val); , int pcibios_write_config_dword (unsigned char bus, ,                                 unsigned char function, ,                                 unsigned char where, ,                                 unsigned int val);

Write 1, 2, or 4 bytes to the configuration space. The device is identified by bus and function as usual, and the value being written is passed as val. The word and dword functions convert the value to little-endian before writing to the peripheral device.

The best way to access the configuration variables is to use the symbolic names defined in <linux/pci.h>. For example, the following two-liner retrieves the revision ID of a device by passing the symbolic name for where to pcibios_read_config_byte:

unsigned char jail_get_revision(unsigned char bus, unsigned char fn)
{
    unsigned char *revision;

    pcibios_read_config_byte(bus, fn, PCI_REVISION_ID, &revision);
    return revision;
}

When accessing multi-byte values, the programmer must remember to watch out for byte-order problems.

Looking at a configuration snapshot

If you want to browse the configuration space of the PCI devices on your system, you can compile and load the module pci/pcidata.c, in the source files provided on the O’Reilly FTP site.

This module creates a dynamic /proc/pcidata file containing a binary snapshot of the configuration space for your PCI devices. The snapshot is updated every time the file is read. The size of /proc/pcidata is limited to PAGE_SIZE bytes (this is a limitation on dynamic /proc files introduced in Section 4.2.1 in Chapter 4). Thus, it lists only the configuration memory for the first PAGE_SIZE/256 devices, which means 16 or 32 devices (probably plenty for your system). I chose to make /proc/pcidata a binary file because of this size constraint, instead of making it a text file like other /proc files.

Another limitation of pcidata is that it scans only the first PCI bus on the system. If your computer includes bridges to other PCI buses, pcidata ignores them.

Devices appear in /proc/pcidata in the opposite order than they appear in /proc/pci. This happens because /proc/pci reads a linked list that grows from the head, while /proc/pcidata is a simple look-up loop that dumps everything in the order it is retrieved.

For example, my frame grabber appears second in /proc/pcidata and (currently) has the following configuration registers:

morgana% dd bs=256 skip=1 count=1 if=/proc/pcidata | od -Ax -t x1
1+0 records in
1+0 records out
000000 86 80 23 12 06 00 00 02 00 00 00 04 00 20 00 00
000010 00 00 00 f1 00 00 00 00 00 00 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000030 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000100

If you juxtapose the dump above and Figure 15.1, you’ll be able to make some sense out of the numbers. Alternatively, you can use the pcidump program, also found on the FTP site, which formats and labels the output listing.

The pcidump code is not worth including here, because the program is simply a long table, plus 10 lines of code that scan the table. Instead, let’s look at some selected output lines:

morgana% dd bs=256 skip=1 count=1 if=/proc/pcidata | ./pcidump
1+0 records in
1+0 records out
        Compulsory registers:
Vendor id: 8086
Device id: 1223
I/O space enabled: n
Memory enabled: y
Master enabled: y
Revision id (decimal): 0
Programmer Interface: 00
Class of device: 0400
Header type: 00
Multi function device: n
        Optional registers:
Base Address 0: f1000000
Base Address 0 Is I/O: n
Base Address 0 is 64-bits: n
Base Address 0 is below-1M: n
Base Address 0 is prefetchable: n
Does generate interrupts: y
Interrupt line (decimal): 10
Interrupt pin (decimal): 1

pcidata and pcidump, used with grep, can be useful tools for debugging a driver’s initialization code. Note, however, that the pcidata.c module is subject to the GPL, because I took the PCI scanning loop from the kernel sources. This shouldn’t matter to you as a driver writer, because I’ve included the module in the source files only as a support utility, not as a template to be reused in new drivers.

Accessing the I/O and Memory Spaces

A PCI peripheral implements six address regions. Each region consists of either memory or I/O locations, or it doesn’t exist. Most devices substitute a memory region for their I/O ports because some processors (like the Alpha) have no native I/O space and because the I/O space on the PC is quite congested. The structural difference between memory and I/O space has been addressed by implementing a ``memory-is-prefetchable'' bit;[39] peripherals that map their control registers to a memory address range declare that range as non-prefetchable, whereas something like video memory on PCI boards is prefetchable. In this section, I’m using the word ``region'' to refer to a PCI address range whenever the discussion applies to either memory or I/O.

An interface board reports the size and current location of its regions using configuration registers--the six 32-bit registers shown in Figure 15.1 whose symbolic names are PCI_BASE_ADDRESS_0 through PCI_BASE_ADDRESS_5. Since the I/O space defined by PCI is a 32-bit address space, it does make sense to use the same configuration interface for memory and I/O. If the device uses a 64-bit address bus, it can declare regions in the 64-bit memory space by using two consecutive PCI_BASE_ADDRESS registers for each region. It is possible for one device to offer both 32-bit regions and 64-bit regions.

I won’t go into detail here, because if you’re going to write a PCI driver, you will need the hardware manual for the device anyway. In particular, I am not going to use either the prefetchable bit here or the two ``type'' bits of the registers, and I’ll limit the discussion to 32-bit peripherals. It’s nonetheless interesting to see how things are implemented in the general case and how Linux drivers deal with PCI memory.

The PCI specs state that each implemented region must be mapped to a configurable address. This means that the device must be equipped with a programmable 32-bit address decoder for each region it implements, and a 64-bit programmable decoder must be present in any board that exploits the 64-bit PCI extension. While 64-bit PCI buses don’t exist on PCs yet, some Alpha workstations have them.

The actual implementation and use of a programmable decoder is simplified by the fact that usually the number of bytes in a region is a power of two; for example 32, 64, 4KB, or 2MB. Moreover, it wouldn’t make much sense to map a region to an unaligned address; 1MB regions naturally align at an address that is a multiple of 1M, and 32-byte regions at a multiple of 32. The PCI specification exploits this alignment; it states that the address decoder must look only at the high bits of the address bus and that only the high bits are programmable. This convention also means that the size of any region must be a power of two.

Remapping a PCI region is thus performed by setting a suitable value in the high bits of a configuration register. For example, a 1M region, which has 20 bits of address space, is remapped by setting the high 12 bits of the register; writing 0x008 xxxxx to the register tells the board to respond to the 8MB-9MB address range. In practice, only very high addresses are used to map PCI regions.

This ``partial decoding'' technique has the additional advantage that the software can determine the size of a PCI region by checking the number of non-programmable bits in the configuration register. To this end, the PCI standard states that unused bits must always read as 0. By imposing a minimum size of 8 bytes for I/O regions and 16 bytes for memory regions, the standard can fit some extra information into the same PCI register: the ``space'' bit, which says whether the region is memory or I/O; the two ``type'' bits; and the prefetchable bit, which is defined only for memory. The type bits select between a 32-bit region, a 64-bit region, and a ``32-bit region that must be mapped below one meg.'' That last value is used for obsolete software that still runs on some PCs.

Detecting the size of a PCI region is simplified by using several bitmasks defined in <linux/pci.h>: PCI_BASE_ADDRESS_SPACE is set if this is a memory region, PCI_BASE_ADDRESS_MEM_MASK masks out the configuration bits for memory regions, and PCI_BASE_ADDRESS_IO_MASK masks out the bits for I/O regions.

Typical code for reporting the current location and size of the PCI regions looks like this:

static u32 addresses[] = {
    PCI_BASE_ADDRESS_0,
    PCI_BASE_ADDRESS_1,
    PCI_BASE_ADDRESS_2,
    PCI_BASE_ADDRESS_3,
    PCI_BASE_ADDRESS_4,
    PCI_BASE_ADDRESS_5,
    0
};

int pciregions_read_proc(char *buf, char **start, off_t offset,
                   int len, int unused)
{
    #define PRINTF(fmt, args...) sprintf(buf+len, fmt, ## args)
    len=0;

    /* Loop through the devices (code not printed in the book) */

        /* A device was found: print its regions */
        for (i=0; addresses[i]; i++) {
            u32 curr, mask;
            char *type;

            pcibios_read_config_dword(bus,fun,addresses[i],&curr);
            cli();
            pcibios_write_config_dword(bus,fun,addresses[i],~0);
            pcibios_read_config_dword(bus,fun,addresses[i],&mask);

            
            pcibios_write_config_dword(bus,fun,addresses[i],curr);
            sti();
            
            len += PRINTF("	region %i: mask 0x%08lx, now at 0x%08lx
",
                          i, (unsigned long)mask, (unsigned long)curr);
            if (!mask) {
                len += PRINTF("	region %i not existent
", i);
                break;
            }
            /* extract the type, and the programmable bits */
            if (mask & PCI_BASE_ADDRESS_SPACE) {
                type = "I/O"; mask &= PCI_BASE_ADDRESS_IO_MASK;
            } else {
                type = "mem"; mask &= PCI_BASE_ADDRESS_MEM_MASK;
            }
            len += PRINTF("	region %i: type %s, size %i
", i,
                          type, ~mask+1);
        }
    return len;
}



This code is part of the pciregions module, distributed in the same directory as pcidata; the module creates a /proc/pciregions file, using the code shown above to generate data. Interrupt reporting is disabled while the configuration register is being modified, to prevent a driver from accessing the region while it is mapped to the wrong place. cli is used instead of save_flags because the function is executed only during the read system calls, and interrupts are known to be enabled during system calls.

Here, for example, is what /proc/pciregions reports for my frame grabber:

Bus 0, device 13, fun  0 (id 8086-1223)
        region 0: mask 0xfffff000, now at 0xf1000000
        region 0: type mem, size 4096
        region 1: mask 0x00000000, now at 0x00000000
        region 1 not existent

The computer’s firmware uses a loop like the one shown earlier to correctly map the regions at boot time. Since the firmware prevents any collision in address assignment, Linux drivers don’t usually change the mappings of the PCI ranges.

It’s interesting to note that the memory size reported by the program above can be overstated. For instance, /proc/pciregions reports that my video board is a 16MB device. This isn’t currently true (although I could expand my video RAM). However, since the size information is used only by the firmware to allocate address ranges, region oversizing is not a problem for the driver writer who knows the internals of the device and can correctly deal with the address range assigned by the firmware.

PCI Interrupts

As far as interrupts are concerned, PCI is easy to handle. The computer’s firmware has already assigned a unique interrupt number to the device, and the driver just needs to use it. The interrupt number is stored in configuration register 60 (PCI_INTERRUPT_LINE), which is one byte wide. This allows for as many as 256 interrupt lines, but the actual limit depends on the CPU being used. The driver doesn’t need to bother checking the interrupt number, as the value found in PCI_INTERRUPT_LINE is guaranteed to be the right one.

If the device doesn’t support interrupts, register 61 (PCI_INTERRUPT_PIN) is 0; otherwise, it’s non-zero. However, since the driver knows if its device is interrupt-driven or not, it doesn’t usually need to read PCI_INTERRUPT_PIN.

Thus, PCI-specific code for dealing with interrupts just needs to read the configuration byte to obtain the interrupt number, as shown in the code below. Otherwise, the information in Chapter 9 applies.

result = pcibios_read_config_byte(bus, fnct, PCI_INTERRUPT_LINE,
                                  &my_irq);
if (result) { /* deal with error */ }

The rest of this section provides additional information for the curious reader, but isn’t needed for writing drivers.

A PCI connector has four interrupt pins, and peripheral boards can use any or all of them. Each pin is individually routed to the motherboard’s interrupt controller, so interrupts can be shared without any electrical problem. The interrupt controller is then responsible for mapping the interrupt wires (pins) to the processor’s hardware; this platform-dependent operation is left to the controller in order to achieve platform independence in the bus itself.

The read-only configuration register located at PCI_INTERRUPT_PIN is used to tell the computer which single pin is actually used. It’s worth remembering that each device board can host up to 8 devices; each device uses a single interrupt pin and reports it in its own configuration register. Different devices on the same device board can use different interrupt pins or share the same one.

The PCI_INTERRUPT_LINE register, on the other hand, is read/write. When the computer is booted, the firmware scans its PCI devices and sets the register for each device according to how the interrupt pin is routed for its PCI slot. The value is assigned by the firmware because only the firmware knows how the motherboard routes the different interrupt pins to the processor. For the device driver, however, the PCI_INTERRUPT_LINE register is read-only.



[38] You’ll find the ID of any device in its own hardware manual.

[39] The information lives in one of the low-order bits of the base-address PCI registers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.14.63