linux中pci设备知识

linux中pci设备知识

Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分。PCI(Periheral Component Interconnect)有三种地址空间:PCI I/O空间、PCI内存地址空间和PCI配置空间。其中,PCI I/O空间和PCI内存地址空间由设备驱动程序使用,而PCI配置空间由Linux PCI初始化代码使用,用于配置PCI设备,比如中断号以及I/O或内存基地址。

1.1.1内核工作

Linux内核主要就做了对PCI设备的枚举和配置;在Linux内核初始化时完成的。

对于PCI总线,有一个叫做PCI桥的设备用来将父总线与子总线连接。作为一种特殊的PCI设备,PCI桥主要包括以下三种:

    1). Host/PCI桥:  用于连接CPU与PCI根总线,第1个根总线的编号为0。在PC中,内存控制器也通常被集成到Host/PCI桥设备芯片中,因此Host/PCI桥通常也被称为“北桥芯片组(North Bridge Chipset)”。

    2). PCI/ISA桥:   用于连接旧的ISA总线。通常,PCI中类似i8359A中断控制器这样的设备也会被集成到PCI/ISA桥设备中。因此,PCI/ISA桥通常也被称为“南桥芯片组(South Bridge Chipset)”

    3). PCI-to-PCI桥(以下称为PCI-PCI桥):  用于连接PCI主总线(Primary Bus)和次总线(Secondary Bus)。PCI-PCI桥所处的PCI总线称为主总线,即次总线的父总线;PCI-PCI桥所连接的PCI总线称为次总线,即主总线的子总线。

 

1.1.2遍历

从Host/PCI桥开始进行探测和扫描,逐个“枚举”连接在第一条PCI总线上的所有设备并记录在案。如果其中的某个设备是PCI-PCI桥,则又进一步再探测和扫描连在这个桥上的次级PCI总线。就这样递归下去,直到穷尽系统中的所有PCI设备。其结果,是在内存中建立起一棵代表着这些PCI总线和设备的PCI树。

每个PCI设备(包括PCI桥设备)都由一个pci_dev结构体来表示,而每条PCI总线则由pci_bus结构来表示。

 

1.1.3配置

PCI设备中一般都带有一些RAM和ROM 空间,通常的控制/状态寄存器和数据寄存器也往往以RAM区间的形式出现,而这些区间的地址在设备内部一般都是从0开始编址的,那么当总线上挂接了多个设备时,对这些空间的访问就会产生冲突。所以,这些地址都要先映射到系统总线上,再进一步映射到内核的虚拟地址空间。

配置就是通过对PCI配置空间的寄存器进行操作从而完成地址的映射。

 

1.1.4数据结构

pci_driver数据结构定义在:include/linux/pci.h文件中。

structpci_driver {

       structlist_head node;

       constchar*name;

       conststructpci_device_id *id_table;  /* must be non-NULL for probe to be called */

       int  (*probe)  (structpci_dev *dev,conststructpci_device_id *id);  /* New device inserted */

       void(*remove) (structpci_dev *dev);  /* Device removed (NULL if not a hot-plug capable driver) */

       int  (*suspend) (structpci_dev *dev, pm_message_t state);     /* Device suspended */

       int  (*suspend_late) (structpci_dev *dev, pm_message_t state);

       int  (*resume_early) (structpci_dev *dev);

       int  (*resume) (structpci_dev *dev);                  /* Device woken up */

       void(*shutdown) (structpci_dev *dev);

       int(*sriov_configure) (structpci_dev *dev,intnum_vfs);/* PF pdev */

       conststructpci_error_handlers *err_handler;

       structdevice_driver    driver;

       structpci_dynids dynids;

};

 

pci_dev也定义在include/linux/pci.h文件中。

详细描述了一个PCI设备几乎所有的硬件信息,包括厂商ID、设备ID、各种资源等.

structpci_dev {

       structlist_head bus_list;     /* node in per-bus list */

       structpci_bus  *bus;          /* bus this device is on */

       structpci_bus  *subordinate;  /* bus this device bridges to */

 

       void            *sysdata;      /* hook for sys-specific extension */

       structproc_dir_entry *procent;/* device entry in /proc/bus/pci */

       structpci_slot *slot;         /* Physical slot this device is in */

 

       unsignedint    devfn;         /* encoded device & function index */

       unsignedshort  vendor;

       unsignedshort  device;

       unsignedshort  subsystem_vendor;

       unsignedshort  subsystem_device;

       unsignedint   class;         /* 3 bytes: (base,sub,prog-if) */

        u8              revision;      /* PCI revision, low byte of class word */

        u8              hdr_type;      /* PCI header type (`multi' flag masked out) */

        u8              pcie_cap;      /* PCI-E capability offset */

        u8              msi_cap;       /* MSI capability offset */

        u8              msix_cap;      /* MSI-X capability offset */

        u8              pcie_mpss:3;   /* PCI-E Max Payload Size Supported */

        u8              rom_base_reg;  /* which config register controls the ROM */

        u8              pin;           /* which interrupt pin this device uses */

        u16             pcie_flags_reg;/* cached PCI-E Capabilities Register */

 

       structpci_driver *driver;     /* which driver has allocated this device */

        u64             dma_mask;      /* Mask of the bits of bus address this

                                           device implements.  Normally this is

                                           0xffffffff.  You only need to change

                                           this if your device has broken DMA

                                           or supports 64-bit transfers.  */

 

       structdevice_dma_parameters dma_parms;

        pci_power_t     current_state; /* Current operating state. In ACPI-speak,

                                           this is D0-D3, D0 being fully functional,

                                           and D3 being off. */

        u8              pm_cap;         /* PM capability offset */

       unsignedint    pme_support:5/* Bitmask of states from which PME#

                                           can be generated */

       unsignedint    pme_interrupt:1;

       unsignedint    pme_poll:1;    /* Poll device's PME status bit */

       unsignedint    d1_support:1;  /* Low power state D1 is supported */

       unsignedint    d2_support:1;  /* Low power state D2 is supported */

       unsignedint    no_d1d2:1;     /* D1 and D2 are forbidden */

        unsignedint    no_d3cold:1;   /* D3cold is forbidden */

       unsignedint    d3cold_allowed:1;      /* D3cold is allowed by user */

       unsignedint    mmio_always_on:1;      /* disallow turning off io/mem

                                                   decoding during bar sizing */

       unsignedint    wakeup_prepared:1;

       unsignedint    runtime_d3cold:1;      /* whether go through runtime

                                                   D3cold, not set for devices

                                                   powered on/off by the

                                                   corresponding bridge */

       unsignedint    d3_delay;      /* D3->D0 transition time in ms */

       unsignedint    d3cold_delay;  /* D3cold->D0 transition time in ms */

 

#ifdef CONFIG_PCIEASPM

       structpcie_link_state  *link_state;   /* ASPM link state. */

#endif

 

        pci_channel_state_t error_state;       /* current connectivity state */

       struct  device  dev;            /* Generic device interface */

 

       int             cfg_size;      /* Size of configuration space */

 

       /*

         * Instead of touching interrupt line and base address registers

         * directly, use the values stored here. They might be different!

         */

       unsignedint    irq;

       structresource resource[DEVICE_COUNT_RESOURCE];/* I/O and memory regions + expansion ROMs */

 

       boolmatch_driver;             /* Skip attaching driver */

       /* These fields are used by common fixups */

       unsignedint    transparent:1/* Transparent PCI bridge */

       unsignedint    multifunction:1;/* Part of multi-function device */

       /* keep track of device state */

       unsignedint    is_added:1;

       unsignedint    is_busmaster:1;/* device is busmaster */

       unsignedint    no_msi:1;      /* device may not use msi */

       unsignedint    block_cfg_access:1;    /* config space access is blocked */

       unsignedint    broken_parity_status:1;/* Device generates false positive parity */

       unsignedint    irq_reroute_variant:2/* device needs IRQ rerouting variant */

       unsignedint    msi_enabled:1;

       unsignedint    msix_enabled:1;

       unsignedint    ari_enabled:1/* ARI forwarding */

       unsignedint    is_managed:1;

       unsignedint    is_pcie:1;     /* Obsolete. Will be removed.

                                           Use pci_is_pcie() instead */

       unsignedint    needs_freset:1;/* Dev requires fundamental reset */

       unsignedint    state_saved:1;

       unsignedint    is_physfn:1;

       unsignedint    is_virtfn:1;

       unsignedint    reset_fn:1;

       unsignedint    is_hotplug_bridge:1;

       unsignedint    __aer_firmware_first_valid:1;

       unsignedint    __aer_firmware_first:1;

       unsignedint    broken_intx_masking:1;

       unsignedint    io_window_1k:1;/* Intel P2P bridge 1K I/O windows */

        pci_dev_flags_t dev_flags;

        atomic_t        enable_cnt;    /* pci_enable_device has been called */

        u32             saved_config_space[16];/* config space saved at suspend time */

       structhlist_head saved_cap_space;

       structbin_attribute *rom_attr;/* attribute descriptor for sysfs ROM entry */

       introm_attr_enabled;          /* has display of the rom attribute been enabled? */

       structbin_attribute *res_attr[DEVICE_COUNT_RESOURCE];/* sysfs file for resources */

       structbin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE];/* sysfs file for WC mapping of resources */

#ifdef CONFIG_PCI_MSI

       structlist_head msi_list;

       structkset *msi_kset;

#endif

       structpci_vpd *vpd;

#ifdef CONFIG_PCI_ATS

       union{

               structpci_sriov *sriov;        /* SR-IOV capability related */

               structpci_dev *physfn;/* the PF this VF is associated with */

        };

       structpci_ats  *ats;  /* Address Translation Service */

#endif

        phys_addr_t rom;/* Physical address of ROM if it's not from the BAR */

       size_tromlen;/* Length of ROM if it's not from the BAR */

};

 

staticinlinestructpci_dev *pci_physfn(structpci_dev *dev)

{

#ifdef CONFIG_PCI_IOV

       if(dev->is_virtfn)

                dev = dev->physfn;

#endif

 

        returndev;

}

 

1.1.5pci初始化流程

pci这块代码在两个地方,一个是driver/pci另一个是arch/x86/pci中。

1.1.5.1 关于入口

pci系统中入口函数处理subsys_initcall之外,还有arch_initcall,postcore_initcall等等。

pci系统的初始化工作有内核来完成,在drivers/pci/probe.c文件中,调用postcore_initcall(pcibus_class_init);函数,在sys/class/下创建一个pci_bus目录

drivers/pci/pci-driver.c文件,postcore_initcall(pci_driver_init); 注册pci总线,并在/sys/bus/下创建了一个pci目录

drivers/pci/pci-acpi.c文件中,调用arch_initcall(acpi_pci_init);

arch/x86/pci/init.c文件中

arch_initcall(pci_arch_init);体系架构相关,对于64 bit x86来说使用CONFIG_PCI_DIRECT的方式进行访问PCI配置空间。在内核编译时候可以指定。

drivers/pci/slot.c文件中,subsys_initcall(pci_slot_init); 创建/sys/bus/slots文件。