PCI设备初始化(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hz5034/article/details/79791240

主要数据结构

pci_bus

struct pci_bus {
   struct list_head node;    /* node in list of buses */          // 同级PCI总线链表
   struct pci_bus *parent;   /* parent bus this bridge is on */   // 上级PCI总线
   struct list_head children; /* list of child buses */           // 下级PCI总线链表
   struct list_head devices;  /* list of devices on this bus */   // PCI总线上设备链表
   struct pci_dev *self;    /* bridge device as seen by parent */ // 所挂PCI桥
   struct list_head slots;       /* list of slots on this bus */
   struct resource    *resource[PCI_BUS_NUM_RESOURCES];           // 总线地址空间
               /* address space routed to this bus */

   struct pci_ops *ops;     /* configuration access functions */
   void      *sysdata;  /* hook for sys-specific extension */
   struct proc_dir_entry *procdir;    /* directory entry in /proc/bus/pci */

   unsigned char  number;       /* bus number */                  // 总线号
   unsigned char  primary;   /* number of primary bridge */
   unsigned char  secondary; /* number of secondary bridge */
   unsigned char  subordinate;   /* max number of subordinate buses */
   ...
};

pci_dev

struct pci_dev {
   struct list_head bus_list; /* node in per-bus list */ // 所挂PCI总线上设备链表
   struct pci_bus *bus;     /* bus this device is on */  // 所挂PCI总线
   struct pci_bus *subordinate;  /* bus this device bridges to */

   void      *sysdata;  /* hook for sys-specific extension */
   struct proc_dir_entry *procent;    /* device entry in /proc/bus/pci */
   struct pci_slot    *slot;    /* Physical slot this device is in */

   unsigned int   devfn;    /* encoded device & function index */ // 设备号+功能号(逻辑设备号)
   unsigned short vendor;                                         // 厂商号
   unsigned short device;                                         // 设备号
   unsigned short subsystem_vendor;                               // 子系统厂商号
   unsigned short subsystem_device;                               // 子系统设备号
   ...
   // resource数组大小为12,每个resource对应PCI设备的一段存储空间
   struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
   ...
};

resource

struct resource {
   resource_size_t start;
   resource_size_t end;
   const char *name;
   unsigned long flags;
   struct resource *parent, *sibling, *child;
};

主要函数

pci_direct_probe()

申请IO地址空间0xCF8~0xCFF

int __init pci_direct_probe(void)
{
   struct resource *region, *region2;

   if ((pci_probe & PCI_PROBE_CONF1) == 0)
      goto type2;
   // 在IO地址空间中从0xCF8开始申请8个字节,名称为"PCI conf1"
   region = request_region(0xCF8, 8, "PCI conf1");
   if (!region)
      goto type2;

   if (pci_check_type1()) {
      raw_pci_ops = &pci_direct_conf1; // 使用pci_direct_conf1函数表
      port_cf9_safe = true;
      return 1;
   }
   release_resource(region);

 type2:
   if ((pci_probe & PCI_PROBE_CONF2) == 0)
      return 0;
   region = request_region(0xCF8, 4, "PCI conf2");
   if (!region)
      return 0;
   region2 = request_region(0xC000, 0x1000, "PCI conf2");
   if (!region2)
      goto fail2;

   if (pci_check_type2()) {
      raw_pci_ops = &pci_direct_conf2;
      port_cf9_safe = true;
      return 2;
   }

   release_resource(region2);
 fail2:
   release_resource(region);
   return 0;
}

pci_sanity_check()

探测Host-PCI桥

static int __init pci_sanity_check(struct pci_raw_ops *o)
{
   u32 x = 0;
   int year, devfn;

   if (pci_probe & PCI_NO_CHECKS)
      return 1;
   /* Assume Type 1 works for newer systems.
      This handles machines that don't have anything on PCI Bus 0. */
   dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
   if (year >= 2001)
      return 1;

   for (devfn = 0; devfn < 0x100; devfn++) { // 遍历逻辑设备号(设备号+功能号),从0到255
      /* static int pci_conf1_read(unsigned int seg,
                                   unsigned int bus,
                                   unsigned int devfn,
                                   int reg,
                                   int len,
                                   u32 *value) */
      // 将总线号为0、逻辑设备号为devfn的逻辑设备的PCI_CLASS_DEVICE(2字节)读入x
      if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
         continue;
      // 若类型是Host-PCI桥
      if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
         return 1;

      // 将PCI_VENDOR_ID(2字节)读入x
      if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
         continue;
      // 若厂商是intel或compaq
      if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
         return 1;
   }

   DBG(KERN_WARNING "PCI: Sanity check failed\n");
   return 0;
}

pci_scan_child_bus()

深度优先搜索总线树上的所有设备

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
   unsigned int devfn, pass, max = bus->secondary;
   struct pci_dev *dev;

   pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);

   /* Go find them, Rover! */
   for (devfn = 0; devfn < 0x100; devfn += 8) // 遍历设备号
      pci_scan_slot(bus, devfn);

   /* Reserve buses for SR-IOV capability. */
   max += pci_iov_bus_range(bus);

   /*
    * After performing arch-dependent fixup of the bus, look behind
    * all PCI-to-PCI bridges on this bus.
    */
   if (!bus->is_added) {
      pr_debug("PCI: Fixups for bus %04x:%02x\n",
          pci_domain_nr(bus), bus->number);
      pcibios_fixup_bus(bus);
      if (pci_is_root_bus(bus))
         bus->is_added = 1;
   }

   for (pass=0; pass < 2; pass++)
      list_for_each_entry(dev, &bus->devices, bus_list) {
         if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
             dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
            max = pci_scan_bridge(bus, dev, max, pass); // 扫描PCI桥,在pci_scan_bridge()中递归调用pci_scan_child_bus()
      }

   /*
    * We've scanned the bus and so we know all about what's on
    * the other side of any bridges that may be on this bus plus
    * any devices.
    *
    * Return how far we've got finding sub-buses.
    */
   pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
      pci_domain_nr(bus), bus->number, max);
   return max;
}

__pci_read_base()

读取PCI设备的BAR中的起始地址和长度,写入resource

int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
         struct resource *res, unsigned int pos)
{
   u32 l, sz, mask;

   mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;

   res->name = pci_name(dev);

   pci_read_config_dword(dev, pos, &l); // 读BAR到l(起始地址)
   pci_write_config_dword(dev, pos, mask); // 写BAR全1
   pci_read_config_dword(dev, pos, &sz); // 读BAR到sz(长度)
   pci_write_config_dword(dev, pos, l); // 写BAR l(恢复)

   /*
    * All bits set in sz means the device isn't working properly.
    * If the BAR isn't implemented, all bits must be 0.  If it's a
    * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
    * 1 must be clear.
    */
   if (!sz || sz == 0xffffffff)
      goto fail;

   /*
    * I don't know how l can have all bits set.  Copied from old code.
    * Maybe it fixes a bug on some ancient platform.
    */
   if (l == 0xffffffff)
      l = 0;

   if (type == pci_bar_unknown) {
      type = decode_bar(res, l);
      res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
      if (type == pci_bar_io) {
         l &= PCI_BASE_ADDRESS_IO_MASK;
         mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;
      } else {
         l &= PCI_BASE_ADDRESS_MEM_MASK;
         mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
      }
   } else {
      res->flags |= (l & IORESOURCE_ROM_ENABLE);
      l &= PCI_ROM_ADDRESS_MASK;
      mask = (u32)PCI_ROM_ADDRESS_MASK;
   }

   if (type == pci_bar_mem64) {
      u64 l64 = l;
      u64 sz64 = sz;
      u64 mask64 = mask | (u64)~0 << 32;

      pci_read_config_dword(dev, pos + 4, &l);
      pci_write_config_dword(dev, pos + 4, ~0);
      pci_read_config_dword(dev, pos + 4, &sz);
      pci_write_config_dword(dev, pos + 4, l);

      l64 |= ((u64)l << 32);
      sz64 |= ((u64)sz << 32);

      sz64 = pci_size(l64, sz64, mask64);

      if (!sz64)
         goto fail;

      if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
         dev_err(&dev->dev, "can't handle 64-bit BAR\n");
         goto fail;
      } else if ((sizeof(resource_size_t) < 8) && l) {
         /* Address above 32-bit boundary; disable the BAR */
         pci_write_config_dword(dev, pos, 0);
         pci_write_config_dword(dev, pos + 4, 0);
         res->start = 0;
         res->end = sz64;
      } else {
         res->start = l64;
         res->end = l64 + sz64;
         dev_printk(KERN_DEBUG, &dev->dev,
            "reg %x %s: %pR\n", pos,
             (res->flags & IORESOURCE_PREFETCH) ?
               "64bit mmio pref" : "64bit mmio",
             res);
      }

      res->flags |= IORESOURCE_MEM_64;
   } else {
      sz = pci_size(l, sz, mask);

      if (!sz)
         goto fail;

      res->start = l; // BAR中的起始地址
      res->end = l + sz; // BAR中的结束地址

      dev_printk(KERN_DEBUG, &dev->dev, "reg %x %s: %pR\n", pos,
         (res->flags & IORESOURCE_IO) ? "io port" :
          ((res->flags & IORESOURCE_PREFETCH) ?
             "32bit mmio pref" : "32bit mmio"),
         res);
   }

 out:
   return (type == pci_bar_mem64) ? 1 : 0;
 fail:
   res->flags = 0;
   goto out;
}

pci_read_bridge_bases()

读取PCI桥的过滤窗口的起始地址和长度,写入resource

void __devinit pci_read_bridge_bases(struct pci_bus *child)
{
   struct pci_dev *dev = child->self;
   u8 io_base_lo, io_limit_lo;
   u16 mem_base_lo, mem_limit_lo;
   unsigned long base, limit;
   struct resource *res;
   int i;

   if (pci_is_root_bus(child))    /* It's a host bus, nothing to read */
      return;

   if (dev->transparent) {
      dev_info(&dev->dev, "transparent bridge\n");
      for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
         child->resource[i] = child->parent->resource[i - 3];
   }

   // 读取IO地址窗口(起始地址和长度)
   res = child->resource[0];
   pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
   pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
   base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
   limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;

   if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
      u16 io_base_hi, io_limit_hi;
      pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
      pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
      base |= (io_base_hi << 16);
      limit |= (io_limit_hi << 16);
   }

   if (base <= limit) {
      res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
      if (!res->start)
         res->start = base;
      if (!res->end)
         res->end = limit + 0xfff;
      dev_printk(KERN_DEBUG, &dev->dev, "bridge io port: %pR\n", res);
   }

   // 读取存储器地址窗口(起始地址和长度)
   res = child->resource[1];
   pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
   pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
   base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
   limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
   if (base <= limit) {
      res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
      res->start = base;
      res->end = limit + 0xfffff;
      dev_printk(KERN_DEBUG, &dev->dev, "bridge 32bit mmio: %pR\n",
         res);
   }

   // 读取"可预取"存储器地址窗口(起始地址和长度)
   res = child->resource[2];
   pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
   pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
   base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
   limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;

   if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
      u32 mem_base_hi, mem_limit_hi;
      pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
      pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);

      /*
       * Some bridges set the base > limit by default, and some
       * (broken) BIOSes do not initialize them.  If we find
       * this, just assume they are not being used.
       */
      if (mem_base_hi <= mem_limit_hi) {
#if BITS_PER_LONG == 64
         base |= ((long) mem_base_hi) << 32;
         limit |= ((long) mem_limit_hi) << 32;
#else
         if (mem_base_hi || mem_limit_hi) {
            dev_err(&dev->dev, "can't handle 64-bit "
               "address space for bridge\n");
            return;
         }
#endif
      }
   }
   if (base <= limit) {
      res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
                IORESOURCE_MEM | IORESOURCE_PREFETCH;
      if (res->flags & PCI_PREF_RANGE_TYPE_64)
         res->flags |= IORESOURCE_MEM_64;
      res->start = base;
      res->end = limit + 0xfffff;
      dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n",
         (res->flags & PCI_PREF_RANGE_TYPE_64) ? "64" : "32",
         res);
   }
}

alloc_resource()

若申请子区间失败,设置区间的起始地址为0

static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
{
   struct resource *pr, *r = &dev->resource[idx];

   pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
       pci_name(dev), idx,
       (unsigned long long)r->start,
       (unsigned long long)r->end,
       (unsigned int)r->flags);

   pr = pci_find_parent_resource(dev, r);
   if (!pr || (pr->flags & IORESOURCE_UNSET) ||
       request_resource(pr, r) < 0) { // 若没找到满足需要的父区间、IORESOURCE_UNSET、申请子区间失败
      printk(KERN_WARNING "PCI: Cannot allocate resource region %d"
             " of device %s, will remap\n", idx, pci_name(dev));
      if (pr)
         pr_debug("PCI:  parent is %p: %016llx-%016llx [%x]\n",
             pr,
             (unsigned long long)pr->start,
             (unsigned long long)pr->end,
             (unsigned int)pr->flags);
      /* We'll assign a new address later */
      r->flags |= IORESOURCE_UNSET;
      r->end -= r->start;
      r->start = 0; // 设置区间的起始地址为0
   }
}

find_resource()

根据resource的长度分配总线地址,写入resource(真正分配总线地址的函数

static int find_resource(struct resource *root, struct resource *new,
          resource_size_t size, resource_size_t min,
          resource_size_t max, resource_size_t align,
          void (*alignf)(void *, struct resource *,
               resource_size_t, resource_size_t),
          void *alignf_data)
{
   struct resource *this = root->child; // this指向子区间链表

   new->start = root->start; // 设置new->start为root->start
   /*
    * Skip past an allocated resource that starts at 0, since the assignment
    * of this->start - 1 to new->end below would cause an underflow.
    */
   if (this && this->start == 0) { // 若第一个子区间的起始地址为0
      new->start = this->end + 1; // 设置new->start为第一个子区间的结束地址+1
      this = this->sibling; // this向右移动
   }
   for(;;) {
      if (this) // 若this非空,设置new->end为this指向子区间的起始地址-1
         new->end = this->start - 1;
      else // 若this为空,设置new->end为root->end
         new->end = root->end;
      if (new->start < min)
         new->start = min;
      if (new->end > max)
         new->end = max;
      new->start = ALIGN(new->start, align); // 地址对齐
      if (alignf)
         alignf(alignf_data, new, size, align);
      if (new->start < new->end && new->end - new->start >= size - 1) { // 找到足够大的子区间
         new->end = new->start + size - 1;
         return 0;
      }
      if (!this) // this为空跳出循环
         break;
      new->start = this->end + 1;
      this = this->sibling; // this向右移动
   }
   return -EBUSY;
}

pci_update_resource()

将分配的总线地址写入BAR

void pci_update_resource(struct pci_dev *dev, int resno)
{
   struct pci_bus_region region;
   u32 new, check, mask;
   int reg;
   enum pci_bar_type type;
   struct resource *res = dev->resource + resno; // 得到dev->resource[resno]

   /*
    * Ignore resources for unimplemented BARs and unused resource slots
    * for 64 bit BARs.
    */
   if (!res->flags)
      return;

   /*
    * Ignore non-moveable resources.  This might be legacy resources for
    * which no functional BAR register exists or another important
    * system resource we shouldn't move around.
    */
   if (res->flags & IORESOURCE_PCI_FIXED)
      return;

   pcibios_resource_to_bus(dev, &region, res); // 根据res得到region

   dev_dbg(&dev->dev, "BAR %d: got res %pR bus [%#llx-%#llx] "
      "flags %#lx\n", resno, res,
       (unsigned long long)region.start,
       (unsigned long long)region.end,
       (unsigned long)res->flags);

   new = region.start | (res->flags & PCI_REGION_FLAG_MASK); // 得到region中的总线地址
   if (res->flags & IORESOURCE_IO)
      mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
   else
      mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;

   reg = pci_resource_bar(dev, resno, &type);
   if (!reg)
      return;
   if (type != pci_bar_unknown) {
      if (!(res->flags & IORESOURCE_ROM_ENABLE))
         return;
      new |= PCI_ROM_ADDRESS_ENABLE;
   }

   pci_write_config_dword(dev, reg, new); // 将总线地址写入BAR
   pci_read_config_dword(dev, reg, &check);

   if ((new ^ check) & mask) {
      dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
         resno, new, check);
   }

   if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
       (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
      new = region.start >> 16 >> 16;
      pci_write_config_dword(dev, reg + 4, new);
      pci_read_config_dword(dev, reg + 4, &check);
      if (check != new) {
         dev_err(&dev->dev, "BAR %d: error updating "
                "(high %#08x != %#08x)\n", resno, new, check);
      }
   }
   res->flags &= ~IORESOURCE_UNSET;
   dev_dbg(&dev->dev, "BAR %d: moved to bus [%#llx-%#llx] flags %#lx\n",
      resno, (unsigned long long)region.start,
      (unsigned long long)region.end, res->flags);
}

配置空间读写函数

在linux-2.6.32.27/include/linux/pci.h中定义了pci_read_config_dword()和pci_write_config_dword(),它们分别调用pci_bus_read_config_dword()和pci_bus_write_config_dword()

static inline int pci_read_config_dword(struct pci_dev *dev, int where,
               u32 *val)
{
   return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val);
}

static inline int pci_write_config_dword(struct pci_dev *dev, int where,
                u32 val)
{
   return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
}

在linux-2.6.32.27/drivers/pci/access.c中通过宏定义了pci_bus_read_config_dword()和pci_bus_write_config_dword()

PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(dword, u32, 4)

#define PCI_OP_READ(size,type,len) \
int pci_bus_read_config_##size \
   (struct pci_bus *bus, unsigned int devfn, int pos, type *value)    \
{                          \
   int res;                     \
   unsigned long flags;                  \
   u32 data = 0;                    \
   if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;  \
   spin_lock_irqsave(&pci_lock, flags);            \
   res = bus->ops->read(bus, devfn, pos, len, &data);    \
   *value = (type)data;                  \
   spin_unlock_irqrestore(&pci_lock, flags);        \
   return res;                      \
}

#define PCI_OP_WRITE(size,type,len) \
int pci_bus_write_config_##size \
   (struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{                          \
   int res;                     \
   unsigned long flags;                  \
   if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;  \
   spin_lock_irqsave(&pci_lock, flags);            \
   res = bus->ops->write(bus, devfn, pos, len, value);       \
   spin_unlock_irqrestore(&pci_lock, flags);        \
   return res;                      \
}

在linux-2.6.32.27/arch/x86/pci/common.c中定义了pci_root_ops,同时通过pci_scan_bus_parented()传入pci_root_ops

pci_bus_read_config_dword()和pci_bus_write_config_dword()分别调用pci_read()和pci_write(),pci_read()和pci_write()分别调用raw_pci_ops的read和write,raw_pci_ops在pci_direct_probe()中被初始化为pci_direct_conf1,即pci_read()和pci_write()分别调用pci_conf1_read()和pci_conf1_write()

pci_root_ops

pci_read()和pci_write()

struct pci_ops pci_root_ops = {
   .read = pci_read,
   .write = pci_write,
};

static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
   return raw_pci_read(pci_domain_nr(bus), bus->number,
             devfn, where, size, value);
}

static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
   return raw_pci_write(pci_domain_nr(bus), bus->number,
              devfn, where, size, value);
}

int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
                  int reg, int len, u32 *val)
{
   if (domain == 0 && reg < 256 && raw_pci_ops)
      return raw_pci_ops->read(domain, bus, devfn, reg, len, val);
   if (raw_pci_ext_ops)
      return raw_pci_ext_ops->read(domain, bus, devfn, reg, len, val);
   return -EINVAL;
}

int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
                  int reg, int len, u32 val)
{
   if (domain == 0 && reg < 256 && raw_pci_ops)
      return raw_pci_ops->write(domain, bus, devfn, reg, len, val);
   if (raw_pci_ext_ops)
      return raw_pci_ext_ops->write(domain, bus, devfn, reg, len, val);
   return -EINVAL;
}

pci_direct_conf1

pci_conf1_read()和pci_conf1_write()

struct pci_raw_ops pci_direct_conf1 = {
   .read =       pci_conf1_read,
   .write =   pci_conf1_write,
};

static int pci_conf1_read(unsigned int seg, unsigned int bus,
           unsigned int devfn, int reg, int len, u32 *value)
{
   unsigned long flags;

   if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
      *value = -1;
      return -EINVAL;
   }

   spin_lock_irqsave(&pci_config_lock, flags);

   // 通过outl写32位io端口
   outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

   switch (len) {
   case 1:
      *value = inb(0xCFC + (reg & 3));
      break;
   case 2:
      *value = inw(0xCFC + (reg & 2));
      break;
   case 4:
      // 通过inl读32位io端口
      *value = inl(0xCFC);
      break;
   }

   spin_unlock_irqrestore(&pci_config_lock, flags);

   return 0;
}

static int pci_conf1_write(unsigned int seg, unsigned int bus,
            unsigned int devfn, int reg, int len, u32 value)
{
   unsigned long flags;

   if ((bus > 255) || (devfn > 255) || (reg > 4095))
      return -EINVAL;

   spin_lock_irqsave(&pci_config_lock, flags);

   // 通过outl写32位io端口
   outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

   switch (len) {
   case 1:
      outb((u8)value, 0xCFC + (reg & 3));
      break;
   case 2:
      outw((u16)value, 0xCFC + (reg & 2));
      break;
   case 4:
      // 通过outl写32位io端口
      outl((u32)value, 0xCFC);
      break;
   }

   spin_unlock_irqrestore(&pci_config_lock, flags);

   return 0;
}

// 生成综合地址
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
   (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
   | (devfn << 8) | (reg & 0xFC))

ioport/iomem_resource资源树

__request_region()

__request_region递归查询ioport/iomem_resource资源树,将resource插入合适的位置

struct resource * __request_region(struct resource *parent,
               resource_size_t start, resource_size_t n,
               const char *name, int flags)
{
   struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);

   if (!res)
      return NULL;

   res->name = name;
   res->start = start;
   res->end = start + n - 1;
   res->flags = IORESOURCE_BUSY;
   res->flags |= flags;

   write_lock(&resource_lock);

   for (;;) {
      struct resource *conflict;

      conflict = __request_resource(parent, res); // 调用__request_resource()
      if (!conflict)
         break;
      if (conflict != parent) {
         parent = conflict;
         if (!(conflict->flags & IORESOURCE_BUSY)) // IORESOURCE_BUSY表示驱动已经标记这个resource为busy
            continue; // 递归调用__request_resource()
      }

      /* Uhhuh, that didn't work out.. */
      kfree(res);
      res = NULL;
      break;
   }
   write_unlock(&resource_lock);
   return res;
}

__request_resource()

将当前区间插入子区间链表,返回值有三种:

  • NULL,表示成功插入子区间链表
  • 父区间,表示父区间不满足要求
  • 子区间,表示父区间满足要求,但当前区间和子区间有交集

返回子区间后,__request_region会递归调用__request_resource()

static struct resource * __request_resource(struct resource *root, struct resource *new)
{
   resource_size_t start = new->start;
   resource_size_t end = new->end;
   struct resource *tmp, **p;

   if (end < start)
      return root;
   if (start < root->start)
      return root;
   if (end > root->end)
      return root;
   p = &root->child; // *p指向子区间链表
   for (;;) {
      tmp = *p; // tmp依次遍历链表
      if (!tmp || tmp->start > end) {
         // 将new插到tmp前面,return NULL
         new->sibling = tmp;
         *p = new;
         new->parent = root;
         return NULL;
      }
      p = &tmp->sibling; // *p向右移动
      if (tmp->end < start)
         continue;
      // 若tmp ->start <= end和tmp->end >= start(即两者有交集),return tmp
      return tmp;
   }
}

猜你喜欢

转载自blog.csdn.net/hz5034/article/details/79791240
今日推荐