linux设备驱动之pci设备的IO和内存_第1页
linux设备驱动之pci设备的IO和内存_第2页
linux设备驱动之pci设备的IO和内存_第3页
linux设备驱动之pci设备的IO和内存_第4页
linux设备驱动之pci设备的IO和内存_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、linux设备驱动之pci设备的I/O和内存 -Pci设备的I/O和内存是一个比较复杂的问题.如下的总线结构:在上图的总线结构中,ethernet设备和pci-pci bridge的同类型资源空间必须要是pci bus0的一个子集例如,pci bus 0的I/O端口资源是0x00CC0x01CC. Ethernet设备的I/O范围的是0x00CC0x0xE0.那么pci-pci bridge的I/O端口范围就必须要在0x0xE00x01CC之间.同样,SCSI和VIDEO同类型资源必须要是pci_bus1的子集.pci bus1上有一个pci桥,对应的资源也就是它所连桥上的资源.即pci_bu

2、s-self.也就是说,下层总线的资源是它上层总线资源的子集。上层总线资源是下层总线资源的父集。其实,每个PCI设备的资源地始地址都是由操作系统设置的.在x86上,都由bios设置好了.假若没有bios的时候,我们应该怎么去设置设备的资源起始范围呢?可能在pci枚举完成之后:1:从根总线开始,设置根总线的资源范围是从0开始,到0xFFFF或者0xFFFFFFFF的最大范围.2:对其它的设备,可往其资源寄存器全部写入1,就可以求得该资源项的类型和长度.3:设备从根总线的资源那里分得对应长度的资源.4:如果设备是pci-pci bridge,则递归配置它.可能有人会有这样迷惑,对应于上图,如果pc

3、i-pci bridge的资源大小是N.而SCSI和video资源范围超过了N怎么办呢?我们必须要注意一点,总线的区间是可以自已设定的,而设备资源的区间是在设计的时候就已经确定好了.也就是说,我们可以更改pci device区间的起始地址,但我们不能改变它的大小.因此,出现了上面所说的这种情况.可能是由bios在处理PCI的时候出现了BUG.我们需要调整总线的资源区间.其实对于pci_bus的资源范围就是它的过滤窗口.对于过滤窗口的作用,我们在枚举的时候分析的很清楚了.CPU访问PC过程是这样的(只有一个根总线和pci-pci bridge过滤窗口功能打开的情况:1:cpu向pci发出一个I/

4、O请求.首先经过根总线.它会判断是否在它的资源范围内.如果在它的范围,它就会丢向总线所在的每一个设备.包括pci bridge. 如果没有在根总线的资源范围,则不会处理这个请求.2:如果pci设备判断该地址属于它的资源范围,则处理后发出应答4:pci bridge接收到这个请求,它会判断I/O地址是否在它的资源范围内.如果在它的范围,它会把它丢到它的下层子线.5:下层总线经过经过相同的处理后,就会找到这个PCI设备了一个PCI设备访问其它PCI设备或者其它外设的过程:1:首先这个PCI发出一个请求,这个请求会在总线上广播2:如果要请求的设备是在同级总线,就会产生应答3:请求的设备不是在同层总线

5、,就会进行pci bridge.pci桥判断该请求不在它的范围内(目的地不是它下层的设备,就会将它丢向上层.4:这样辗转之后,就能找到对应的设备了经过这样的分析过来,相信对pci bridge的过滤窗口有更深的理解了.Linux中使用struct resource的结构来表示I/O端口或者是设备内存。定义如下:struct resource resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child;Sta

6、rt: 表示它所占资源的起始地址。End: 表示它所占资源的未尾地址Name: 所占资源的名字Flags: 资源的类型。目前有I/O和内存两种Parent.sibling.child:用来表示资源的所属关系。分别表示它的父结点,兄弟结点和子结点。从前面的分析可以看到,有一些总线可能bios没有遍历到或许bios的处理有错误,所以需要对整个系统的PCI总线和PCI设备的资源占用情况遍历一次。完整的建立上述的struct resource结构(在之前枚举的时候,只是处理了start和end成员.。这个过程是在pcibios_resource_survey( 完成的。如下所示:subsys_init

7、call(pcibios_init;static int _init pcibios_init(void . pcibios_resource_survey(;pcibios_init这个函数是被fs_initcall(所描述的。在kernel启动的时候,会调用宏所描述的函数。在pcibios_init ()又会调用pcibios_assign_resources(),它的代码如下所示:void _init pcibios_resource_survey(void DBG(PCI: Allocating resourcesn; pcibios_allocate_bus_resources(&p

8、ci_root_buses; pcibios_allocate_resources(0; pcibios_allocate_resources(1;它先对总线的资源进行处理。然后再对PCI设备的资源进行处理。我们先看pcibios_allocate_bus_resources(static void _init pcibios_allocate_bus_resources(struct list_head *bus_list struct pci_bus *bus; struct pci_dev *dev; int idx; struct resource *r, *pr; /* Depth-

9、First Search on bus tree */ list_for_each_entry(bus, bus_list, node /pci-bridge if (dev = bus-self for (idx = PCI_BRIDGE_RESOURCES; idx resourceidx; if (!r-flags continue; pr = pci_find_parent_resource(dev, r; if (!r-start | !pr | request_resource(pr, r flags = 0; pcibios_allocate_bus_resources(&bus

10、-children; 这个是一个深度优先搜索算法。类似的算法在pci结构中用得很多。它遍历pci_root_buses中的每一个根总线下面的所有总线。如果该总线有对应的pci-pci bridge,就先处理这个pci桥的资源.PCI桥的资源范围是PCI_BRIDGE_RESOURCES PCI_NUM_RESOURCES.对于它的每个资源区间。都要从它的上层总线中获得.代码中遍历PCI桥的每一个资源区间,然后找到它在上层总线的对应区间。然后为它建立结构关系。pci_find_parent_resource()就是为PCI的资源区间找到一个合适的父结点。代码如下:struct resource

11、*pci_find_parent_resource(const struct pci_dev *dev, struct resource *res const struct pci_bus *bus = dev-bus; int i; struct resource *best = NULL; for(i = 0; i resourcei; if (!r continue; if (res-start & !(res-start = r-start & res-end end continue; /* Not contained */ if (res-flags r-flags & (IORE

12、SOURCE_IO | IORESOURCE_MEM continue; /* Wrong type */ if (!(res-flags r-flags & IORESOURCE_PREFETCH return r; /* Exact match */ if (res-flags & IORESOURCE_PREFETCH & !(r-flags & IORESOURCE_PREFETCH best = r; /* Approximating prefetchable by non-prefetchable */ return best;首先从pci_dev -bus就找到了它的上层总线,每

13、条总线拥有PCI_BUS_NUM_RESOURCES个资源区间. 所要寻找的父结点必须要满足以后几个条件:1:子结点的范围必须要落在父结点的区间范围内2:父子区间的基本类型应该一致。(基本类型即IO或者内存)3:如果父子窗口都是可预读的,就完全匹配了4:如果子结点可预读,而父结点不可预读。也将就着可以了.注意:父结点可预读而子结点不可预读是不允许的。找到它所属的父结点之后,会调用request_resource()从父结点中请求资源。代码如下:int request_resource(struct resource *root, struct resource *new struct reso

14、urce *conflict; write_lock(&resource_lock; conflict = _request_resource(root, new; write_unlock(&resource_lock; return conflict ? -EBUSY : 0;_request_resource()代码如下:static struct resource * _request_resource(struct resource *root, struct resource *new resource_size_t start = new-start; resource_size

15、_t end = new-end; struct resource *tmp, *p; if (end start return root; if (start start return root; if (end root-end return root; p = &root-child; for (; tmp = *p; if (!tmp | tmp-start end new-sibling = tmp; *p = new; new-parent = root; return NULL; p = &tmp-sibling; if (tmp-end start | !pr | reques

16、t_resource(pr, r flags = 0; 也就是说,如果分配失败了,它会将资源的flags标志置为0回到pcibios_resource_survey(中,接着往下来,会发现它以不同的参数调用了pcibios_allocate_resources()两次。跟进这个函数的代码进行分析:static void _init pcibios_allocate_resources(int pass struct pci_dev *dev = NULL; int idx, disabled; u16 command; struct resource *r, *pr; for_each_pci

17、_dev(dev pci_read_config_word(dev, PCI_COMMAND, &command; for (idx = 0; idx resourceidx; if (r-parent /* Already allocated */ continue; if (!r-start /* Address not assigned at all */ continue; if (r-flags & IORESOURCE_IO disabled = !(command & PCI_COMMAND_IO; else disabled = !(command & PCI_COMMAND_

18、MEMORY; /对于已经启用的,在第一次扫描的时候就将其配制 /否则。要等到第二次 if (pass = disabled DBG(PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%dn, r-start, r-end, r-flags, disabled, pass; pr = pci_find_parent_resource(dev, r; if (!pr | request_resource(pr, r end -= r-start; r-start = 0; if (!pass /对于ROM。在第一次扫描时就将它关闭 r = &dev-resour

19、cePCI_ROM_RESOURCE; if (r-flags & IORESOURCE_ROM_ENABLE /* Turn the ROM off, leave the resource region, * but keep it unregistered. */ u32 reg; DBG(PCI: Switching off ROM of %sn, pci_name(dev; r-flags &= IORESOURCE_ROM_ENABLE; pci_read_config_dword(dev, dev-rom_base_reg, pci_write_config_dword(dev,

20、dev-rom_base_reg, reg & PCI_ROM_ADDRESS_ENABLE; 该函数遍历整个pci设备。如果该设备的相应空间已经启用了(I/O或者内存)。那在以0为参数调用的时候就让它分配好资源。对于没有被启用的资源。要等到第二次以1为参数调用参数时才会处理。另外:在第一次处理中就把设备的ROM区间关闭。要等到使用设备的时候再把区间打开。这个打开的过程一般在设备驱动程序里完成。到这里,我们终于知道为什么要用不同的参数调用函数二次。这样做是为了优先让已经被启用的资源从父节点中分得资源。如果资源分配失败了。就会将相应资源的start设为0。End成员则设为这个区间的长度。有必要提

21、一下linux2.4.12中这部份的处理,它是这样子的:对于pci bus的资源分配,如果分配失败.不做任何事情,只是打印出一条警告语句.而在linux2.6.25中.如果pci bus资源分配失败,会将资源的flag置为0.为什么2.6要这样做呢?其实2.4.12中的处理是一个BUG.如果pci bus资源分配失败没有任何的处理的话.那接下来为该总线下面的pci device分配资源的时候,是从该总线的资源中请求的.而事实上,该总线的资源又是有冲突的.这样也造成了该总线下层设备的资源无效.而在linux 2.6中.将分配失败总线的flag置为了0.下层设备在请求资源的分配的时候,就会类型匹配

22、失败,而避免了上面说的这个部问题.这个BUG不知道在后面的版本中有没有被修正 :-pcibios_resource_survey()执先完了之后。Pci的所有总线和设备的资源都被验证分配了一次。对于不能正确分配资源的设备。也做好了标记。接下来,我们来看一下怎么处理资源分配失败的设备。看下面的这段代码:fs_initcall(pcibios_assign_resources;fs_initcall(的优先级比subsys_initcall的优先级小,它在pcibios_init(之后才会得到运行。看一下它的代码:static int _init pcibios_assign_resources(

23、void struct pci_dev *dev = NULL; struct resource *r, *pr; if (!(pci_probe & PCI_ASSIGN_ROMS /* * Try to use BIOS settings for ROMs, otherwise let * pci_assign_unassigned_resources( allocate the new * addresses. */ for_each_pci_dev(dev r = &dev-resourcePCI_ROM_RESOURCE; if (!r-flags | !r-start contin

24、ue; pr = pci_find_parent_resource(dev, r; if (!pr | request_resource(pr, r end -= r-start; r-start = 0; pci_assign_unassigned_resources(; return 0;之前在pcibios_resource_survey()中没有处理ROM的区间。在这里,先遍历每个设备,处理一下它的ROM空间的资源分配。照以前的方式一样,如果资源分配失败,就让它的start置为0。End置为区间的长度。处理完之后,进入到pci_assign_unassigned_resources(。

25、我们希望每一个PCI设备,bios都为我们处理好了。可是总是事与愿违。进行的这里,不得不处理一下之前资源分配失败的设备了。代码如下:void _initpci_assign_unassigned_resources(void struct pci_bus *bus; /* Depth first, calculate sizes and alignments of all subordinate buses. */ list_for_each_entry(bus, &pci_root_buses, node pci_bus_size_bridges(bus; /* Depth last, al

26、locate resources and update the hardware. */ list_for_each_entry(bus, &pci_root_buses, node pci_bus_assign_resources(bus; pci_enable_bridges(bus; 首先我们要处理资源分配失败的pci_bus。在上面的分析中,如果pci bus资源分配失败。就会将其所属资源的flags置为0.对于这些总线,是在第一个循环里处理的.第一个循环,遍历挂在pci_root_buses上的所有根结点。然后调用pci_bus_size_bridges()。代码如下:void _r

27、ef pci_bus_size_bridges(struct pci_bus *bus struct pci_dev *dev; unsigned long mask, prefmask; list_for_each_entry(dev, &bus-devices, bus_list struct pci_bus *b = dev-subordinate; if (!b continue; switch (dev-class 8 case PCI_CLASS_BRIDGE_CARDBUS: pci_bus_size_cardbus(b; break; case PCI_CLASS_BRIDGE

28、_PCI: default: pci_bus_size_bridges(b; break; /* The root bus? */ if (!bus-self return; switch (bus-self-class 8 case PCI_CLASS_BRIDGE_CARDBUS: /* dont size cardbuses yet. */ break; case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus; default: pbus_size_io(bus; /* If the bridge supports prefetcha

29、ble range, size it separately. If it doesnt, or its prefetchable window has already been allocated by arch code, try non-prefetchable range for both types of PCI memory resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; if (pbus_size_mem(bus, prefmask, prefmask mas

30、k = prefmask; /* Success, size non-prefetch only. */ pbus_size_mem(bus, mask, IORESOURCE_MEM; break; 这是一个深度优先搜索算法。首先遍历总线上的所有设备,如果是pci bridge,递归调用pci_bus_size_bridges()处理下层pci bus.对于每一条不是根总线的pci bus都会经过大循环后面的处理,即对应于case PCI_CLASS_BRIDGE_PCI后面的处理.它要经过的第一个函数是pci_bridge_check_ranges(.代码如下:static void pc

31、i_bridge_check_ranges(struct pci_bus *bus u16 io; u32 pmem; struct pci_dev *bridge = bus-self; struct resource *b_res; b_res = &bridge-resourcePCI_BRIDGE_RESOURCES; b_res1.flags |= IORESOURCE_MEM; pci_read_config_word(bridge, PCI_IO_BASE, &io; if (!io pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f

32、0; pci_read_config_word(bridge, PCI_IO_BASE, &io; pci_write_config_word(bridge, PCI_IO_BASE, 0x0; if (io b_res0.flags |= IORESOURCE_IO; /* DECchip 21050 pass 2 errata: the bridge may miss an address disconnect boundary by one PCI data phase. Workaround: do not use prefetching on this device. */ if (

33、bridge-vendor = PCI_VENDOR_ID_DEC & bridge-device = 0x0001 return; pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem; if (!pmem pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0xfff0fff0; pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem; pci_write_config_dword(bridge, PCI_PREF_

34、MEMORY_BASE, 0x0; if (pmem b_res2.flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;Pci bridge设备的7,8,9项是属于过滤窗口,也就是对应于总线的资源。在这里,检查pci bus是否支持I/O和prefetch memory类型的窗口.如果不支持,则相应寄存器是只读的,而且值为0. 顺便说一句,所有的pci bus都是支持memory窗口的.如果相应类型的资源有效则给相应资源的flags置相应的标志。最后还要记得将相关寄存器清空。我们在前面分析过.经过这里的处理,只要判断区间存在,就会置相应的标志。那么,对于

35、我们在上面分配资源失败的pci bus.如果区间确实存在的话。就会重新设置这个标志。重新设置标志的操作是很重要的一个步骤.它能将前面处理时,因资源冲突的pci bus修正过来.运行到这里.怎么区分哪些pci bus是有待修正的,而哪些pci bus是正常的呢?可能根据 resource-parent来判断.如果是正常的pci bus.该成员会指向它的父结点.如果是有待修正的pci bus.它的这个域是空的.也就是说,还没有链接到父结点.回到pci_bus_size_bridges(中,经过pci_bridge_check_ranges(的处理过后,流程会转向pbus_size_io(.从字面意思看这个函数是用来计算i/o的大小.主要是用来处理有待修正的pci bus.我们来思考一下,为什么pci bus的资源会有冲突呢?可能有两个原因:1:pci bus的起始地址冲突.在上层pci bus中,该区间已经被其它设备占用了.这种情况只需要在上层设备中偏

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论