MSI中断与Linux实现

原文: http://blog.csdn.net/Greenland03/archive/2010/09/02/5859949.aspx

1.什么是MSI中断

   Message Signaled Interrupts 是pci2.2中提出来的一种新的中断形式。后续有msi-x扩展。

   msi以及msi-x这种中断形式的一个最主要的特点就是,它在系统的特定地址做一个memory write transaction, 将一个系统约定的数据写入,以此通知CPU一个中断产生了。这个特点带来的最主要的好处就是脱离了传统的interrupt pin的约束,中断的数目也不再受到限制。

2.PCI规范中的MSI设计

    msi以及msi-x的相关数据作为pci配置空间的一个capability structure来实现的。

    msi的capability structure比较简单。

    这是一个最简单的32为message 地址以及16位message 数据的msi capability structure。根据message control中的标记,地址可以是64位,还可能存在masking/pending域,用于对msi中断的屏蔽进行管理。

    msi-x为了扩展支持更多的中断向量,其capability structure包含的是中断向量表的地址(由设备的bar来确定)。

   (address和data的格式根据不同的体系结构有不同的实现方式。最简单的实现就可以是address就是一个固定的地址,data就是为其分配的中断向量号,这样root complex在发现对这个address写了数据,就会通知CPU对应的msi中断到了。当然,我们也可以细分这些数据结构,达到中断检查等目的)

3.Linux 2.6.26中的msi中断处理

   以X86为例。

   一般的流程是,设备驱动里面检查自己是否具有msi或者msi能力,如果有的话,调用driver/pci/msi.c中的pci_enable_msi或者pci_enable_msix。

   这里以msi为例,msix稍微复杂一些,但原理类似。


--------------------------------------------------------------------------------

int pci_enable_msi(struct pci_dev* dev)
{
 int status;

 status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
 if (status)
  return status;

 WARN_ON(!!dev->msi_enabled);

 /* Check whether driver already requested for MSI-X irqs */
 if (dev->msix_enabled) {
  printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
   "Device already has MSI-X enabled\n",
   pci_name(dev));
  return -EINVAL;
 }
 status = msi_capability_init(dev);
 return status;


--------------------------------------------------------------------------------

开始时检查,重点看msi_capability_init


--------------------------------------------------------------------------------

static int msi_capability_init(struct pci_dev *dev)
{
 struct msi_desc *entry;
 int pos, ret;
 u16 control;

 msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */

    pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
 pci_read_config_word(dev, msi_control_reg(pos), &control);
 /* MSI Entry Initialization */
 entry = alloc_msi_entry();
 if (!entry)
  return -ENOMEM;

 entry->msi_attrib.type = PCI_CAP_ID_MSI;
 entry->msi_attrib.is_64 = is_64bit_address(control);
 entry->msi_attrib.entry_nr = 0;
 entry->msi_attrib.maskbit = is_mask_bit_support(control);
 entry->msi_attrib.masked = 1;
 entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
 entry->msi_attrib.pos = pos;
 if (is_mask_bit_support(control)) {
  entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
    is_64bit_address(control));
 }
 entry->dev = dev;
 if (entry->msi_attrib.maskbit) {
  unsigned int maskbits, temp;
  /* All MSIs are unmasked by default, Mask them all */
  pci_read_config_dword(dev,
   msi_mask_bits_reg(pos, is_64bit_address(control)),
   &maskbits);
  temp = (1 << multi_msi_capable(control));
  temp = ((temp - 1) & ~temp);
  maskbits |= temp;
  pci_write_config_dword(dev,
   msi_mask_bits_reg(pos, is_64bit_address(control)),
   maskbits);
  entry->msi_attrib.maskbits_mask = temp;
 }
 list_add_tail(&entry->list, &dev->msi_list);

 /* Configure MSI capability structure */
 ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
 if (ret) {
  msi_free_irqs(dev);
  return ret;
 }

 /* Set MSI enabled bits  */
 pci_intx_for_msi(dev, 0);
 msi_set_enable(dev, 1);
 dev->msi_enabled = 1;

 dev->irq = entry->irq;
 return 0;
}


--------------------------------------------------------------------------------

首先根据message control中的一些标记填写 msi_desc 数据结构。最重要的两个数据结构 address 和data是在arch_setup_msi_irqs中填充的。

对应到x86架构中,这个函数在arch/x86/kernel/io_apic_32.c中。

arch_setup_msi_irq


--------------------------------------------------------------------------------

int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
 struct msi_msg msg;
 int irq, ret;
 irq = create_irq();
 if (irq < 0)
  return irq;

 ret = msi_compose_msg(dev, irq, &msg);
 if (ret < 0) {
  destroy_irq(irq);
  return ret;
 }

 set_irq_msi(irq, desc);
 write_msi_msg(irq, &msg);

 set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq,
          "edge");

 return 0;
}


--------------------------------------------------------------------------------

首先申请一个设备终端号---create_irq();

然后根据设备终端号构建了data—— msi_compose_msg

         看看这个函数里面,可以看到x86下实现的一些属性。

          msg->address_hi = MSI_ADDR_BASE_HI;
          msg->address_lo =  MSI_ADDR_BASE_LO | ((INT_DEST_MODE == 0) ?MSI_ADDR_DEST_MODE_PHYSICAL:

                                 MSI_ADDR_DEST_MODE_LOGICAL) |((INT_DELIVERY_MODE != dest_LowestPrio) ?
                                 MSI_ADDR_REDIRECTION_CPU:MSI_ADDR_REDIRECTION_LOWPRI) |MSI_ADDR_DEST_ID(dest);

          msg->data =  MSI_DATA_TRIGGER_EDGE | MSI_DATA_LEVEL_ASSERT | ((INT_DELIVERY_MODE !=                

                         dest_LowestPrio) ?MSI_DATA_DELIVERY_FIXED:MSI_DATA_DELIVERY_LOWPRI)

                            |MSI_DATA_VECTOR (vector);

         不同架构下可能上述的赋值是不一样的。主要的不同体现在属性上。

然后写到相关的数据结构中。

set_irq_msi

write_msi_msg

set_irq_chip_and_handler_name//这里会提供一个入口,在中断的总入口之后,msi设备中断会进入handle_edge_irq。

至此,msi中断的处理流程介绍完毕。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Greenland03/archive/2010/09/02/5859949.aspx

你可能感兴趣的:(linux)