Configuring the PCI Bus Driver

The PCI Bus driver is in %_WINCEROOT%\Public\Common\Oak\Drivers\PCIbus. It is responsible for enumerating the PCI bus and loading device drivers for all PCI devices it finds and recognizes on the bus. This allows dynamic loading of device drivers for PCI devices. Individual device drivers do not need to enumerate their bus on their own. All of the PCI bus enumeration code is contained in one DLL and all device drivers can reuse this code.

Note   For the CEPC or any platform that uses a BIOS to set up the PCI bus, the PCI bus driver does not need to configure the bus. The PCI bus driver enumerates or reads the state of the bus and provides this information into the Instance key for a device and the driver, and the I/O Resource Manager. You can skip the configuration step by setting NoConfig equal to 1. This value is a subkey of RootKey\PCI. If you need to configure, set NoConfig equal to 0 (zero), or remove it.

The following steps show you how to enable the PCI bus driver and installable interrupt service routines (ISRs) on your platform. For each of these, refer to the ARM Integrator code in %_WINCEROOT%\Platform\ARMINTEGRATOR as an example:

To enable the PCI bus driver

  1. Modify the boot loader and initialization code.

    Typically, the boot loader and startup code for a platform enumerates the entire PCI bus. Modify this functionality so that the boot loader and startup code enumerates only the debugging devices that reside on the PCI bus. This is typically the Ethernet debugging adapter. Serial is usually a legacy ISA port.

    Make sure the enumerator handles the case where the debug device is behind a PCI to PCI bridge or PCI to PCI bridges. The host to PCI bridge is set up with the largest possible windows, while any PCI to PCI bridges between the host and the Ethernet debugging device will be set up with windows that are only as large as the debugging devices. This window starts at the beginning of the host to PCI bridge window. This allows the PCI bus driver to expand this window to accommodate additional devices. If there is more than one debugging device that shares the same resource type, memory or I/O space, their resources must be placed next to each other, or as close as possible allowed by natural alignment.

    Any devices and bridges other than the debugging devices must be disabled by setting the three least significant bits of the command register equal to zero. At power-on, this is the default state.

    The registry needs to be populated with information about the debugging device. This is needed for the PCI bus enumerator to recognize that the device is already set up and is using resources. The PCI_REG_INFO structure is filled with information about the device. This information includes bus, device and function numbers, memory, I/O base addresses, and so on, when the debugging device is set up. See %_WINCEROOT%\Platform\ARMIntegrator\Kernel\HAL\PCIConfig.c for an example. The structure is written to the registry key HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PCI\Instance\KITL in this example with a call to IOCTL_HAL_INITREGISTRY by the kernel.

  2. Set up the host to PCI bridge.

    Typically, the boot loader and startup code have already properly set up the host to PCI bridge. The PCI window must match that of the resources described in the registry under the main PCI key.

  3. Implement OAL functions.

    Implement the following functions in your OAL. These are declared in %_WINCEROOT%\Platform\<Platform Name>\Inc\Oalintr.h.

    Function Description
    OEMGetInterrupt Used by the PCI bus enumerator to request an IRQ for a PCI device.
    OEMRequestSysIntr Used in the OEMIoControl routine to implement IOCTL_HAL_TRANSLATE_IRQ and IOCTL_HAL_REQUEST_SYSINTR.
    OEMTranslateIrq Used by the main ISR to translate a non-shareable IRQ into a SYSINTR, or the last shared IRQ that is not covered by an installable ISR. This is a direct one-to-one mapping of IRQ to SYSINTR.
    OEMTranslateSysIntr Maps a SYSINTR to its corresponding IRQ.
  4. Update OEMIoControl.

    Update OEMIoControl to include IOCTL_HAL_TRANSLATE_IRQ, IOCTL_HAL_REQUEST_SYSINTR, and IOCTL_HAL_REQUEST_IRQ.

    See %_WINCEROOT%\Platform\ARMIntegrator\Kernel\Hal\Oemioctl.c for an example implementation.

    This implementation is also shown in the code example below.

    // Translate IRQ -> SYSINTR (or request new SYSINTR)
    case IOCTL_HAL_TRANSLATE_IRQ:
    case IOCTL_HAL_REQUEST_SYSINTR:
        if (nInBufSize >= sizeof(DWORD) && nOutBufSize >= sizeof(DWORD) && lpOutBuf && lpInBuf) {
            *(PDWORD)lpOutBuf = OEMRequestSysIntr(*(PDWORD)lpInBuf);
    
            if (lpBytesReturned) {
                *lpBytesReturned = sizeof(DWORD);
            }
    
            return TRUE;
        }
    
        DEBUGMSG(1, (TEXT("OEMIoControl(IOCTL_HAL_TRANSLATE_IRQ): Invalid Parameter.\r\n")));
        return FALSE;
    
    // Provide interrupt number based on device location
    case IOCTL_HAL_REQUEST_IRQ:
        if (nInBufSize >= sizeof(DEVICE_LOCATION) && nOutBufSize >= sizeof(DWORD) && lpOutBuf && lpInBuf) {
            retval = OEMGetInterrupt((PDEVICE_LOCATION)lpInBuf, (PDWORD)lpOutBuf);
    
            if (lpBytesReturned) {
                *lpBytesReturned = sizeof(DWORD);
            }
    
            break;
        }
    
        DEBUGMSG(1, (TEXT("OEMIoControl(IOCTL_HAL_TRANSLATE_IRQ): Invalid Parameter.\r\n")));
        return FALSE;
    
  5. Update the main OAL ISR routine.

    Call the chain of installed interrupts first to see if anything claims the interrupt. If nothing does, perform the standard action of translating the IRQ into a SYSINTR and then return. You can slightly optimize this by calling NKCallIntChain only on IRQs for which installable ISRs are allowed.

    See %_WINCEROOT%\Platform\ArmIntegrator\Kernel\Hal\Arm\Intx20t.c for an example implementation.

    This implementation is also shown in the code example below.

    // Call interrupt chain to see if any installed ISRs handle this interrupt
    nSysIntr = NKCallIntChain(nIRQ);
    
    // IRQ not claimed by installed ISR; translate into SYSINTR
    if (nSysIntr == SYSINTR_CHAIN) {
        nSysIntr = OEMTranslateIrq(nIRQ);
    }
    
    // If we have found a valid interrupt, disable it and return SYSINTR
    if (nSysIntr != -1) {
        OEMInterruptDisable(nSysIntr);
        return nSysIntr;
    } else {
        return SYSINTR_NOP;
    }
    
  6. Update Platform.reg and Platform.bib.

    The MemBase, MemLen, IoBase and IoLen values under the PCI key indicate the PCI bus windows used by the system. These are logical, physical, PCI bus-relative addresses. These addresses are not translated. Add PCIbus.dll to Platform.bib, and add the following to Platform.reg. The values for MemBase, MemLen, IoBase, and IoLen are sample values and are not expected to work on all platforms. When NoConfig is set to 1, the MemBase, MemLen, IoBase, and IoLen values are not used.

    [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PCI]
        "Dll"="PCIbus.dll"
        "Order"=dword:20
        "Flags"=dword:1
        "NoConfig"=dword:0
        "MemBase"=dword:40000000
        "MemLen"=dword:20000000
        "IoBase"=dword:00800000
        "IoLen"=dword:00800000
    

See Also

PCI Device Support

 Last updated on Tuesday, May 18, 2004

© 1992-2003 Microsoft Corporation. All rights reserved.