msi.c
来自「linux 内核源代码」· C语言 代码 · 共 732 行 · 第 1/2 页
C
732 行
/* * File: msi.c * Purpose: PCI Message Signaled Interrupt (MSI) * * Copyright (C) 2003-2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */#include <linux/err.h>#include <linux/mm.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/msi.h>#include <linux/smp.h>#include <asm/errno.h>#include <asm/io.h>#include "pci.h"#include "msi.h"static int pci_msi_enable = 1;static void msi_set_enable(struct pci_dev *dev, int enable){ int pos; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (pos) { pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); control &= ~PCI_MSI_FLAGS_ENABLE; if (enable) control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); }}static void msix_set_enable(struct pci_dev *dev, int enable){ int pos; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); if (pos) { pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); control &= ~PCI_MSIX_FLAGS_ENABLE; if (enable) control |= PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); }}static void msix_flush_writes(unsigned int irq){ struct msi_desc *entry; entry = get_irq_msi(irq); BUG_ON(!entry || !entry->dev); switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: /* nothing to do */ break; case PCI_CAP_ID_MSIX: { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; readl(entry->mask_base + offset); break; } default: BUG(); break; }}static void msi_set_mask_bit(unsigned int irq, int flag){ struct msi_desc *entry; entry = get_irq_msi(irq); BUG_ON(!entry || !entry->dev); switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: if (entry->msi_attrib.maskbit) { int pos; u32 mask_bits; pos = (long)entry->mask_base; pci_read_config_dword(entry->dev, pos, &mask_bits); mask_bits &= ~(1); mask_bits |= flag; pci_write_config_dword(entry->dev, pos, mask_bits); } else { msi_set_enable(entry->dev, !flag); } break; case PCI_CAP_ID_MSIX: { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; writel(flag, entry->mask_base + offset); readl(entry->mask_base + offset); break; } default: BUG(); break; } entry->msi_attrib.masked = !!flag;}void read_msi_msg(unsigned int irq, struct msi_msg *msg){ struct msi_desc *entry = get_irq_msi(irq); switch(entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; u16 data; pci_read_config_dword(dev, msi_lower_address_reg(pos), &msg->address_lo); if (entry->msi_attrib.is_64) { pci_read_config_dword(dev, msi_upper_address_reg(pos), &msg->address_hi); pci_read_config_word(dev, msi_data_reg(pos, 1), &data); } else { msg->address_hi = 0; pci_read_config_word(dev, msi_data_reg(pos, 0), &data); } msg->data = data; break; } case PCI_CAP_ID_MSIX: { void __iomem *base; base = entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); break; } default: BUG(); }}void write_msi_msg(unsigned int irq, struct msi_msg *msg){ struct msi_desc *entry = get_irq_msi(irq); switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; pci_write_config_dword(dev, msi_lower_address_reg(pos), msg->address_lo); if (entry->msi_attrib.is_64) { pci_write_config_dword(dev, msi_upper_address_reg(pos), msg->address_hi); pci_write_config_word(dev, msi_data_reg(pos, 1), msg->data); } else { pci_write_config_word(dev, msi_data_reg(pos, 0), msg->data); } break; } case PCI_CAP_ID_MSIX: { void __iomem *base; base = entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET); break; } default: BUG(); } entry->msg = *msg;}void mask_msi_irq(unsigned int irq){ msi_set_mask_bit(irq, 1); msix_flush_writes(irq);}void unmask_msi_irq(unsigned int irq){ msi_set_mask_bit(irq, 0); msix_flush_writes(irq);}static int msi_free_irqs(struct pci_dev* dev);static struct msi_desc* alloc_msi_entry(void){ struct msi_desc *entry; entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL); if (!entry) return NULL; INIT_LIST_HEAD(&entry->list); entry->irq = 0; entry->dev = NULL; return entry;}static void pci_intx_for_msi(struct pci_dev *dev, int enable){ if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)) pci_intx(dev, enable);}#ifdef CONFIG_PMstatic void __pci_restore_msi_state(struct pci_dev *dev){ int pos; u16 control; struct msi_desc *entry; if (!dev->msi_enabled) return; entry = get_irq_msi(dev->irq); pos = entry->msi_attrib.pos; pci_intx_for_msi(dev, 0); msi_set_enable(dev, 0); write_msi_msg(dev->irq, &entry->msg); if (entry->msi_attrib.maskbit) msi_set_mask_bit(dev->irq, entry->msi_attrib.masked); pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); if (entry->msi_attrib.maskbit || !entry->msi_attrib.masked) control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);}static void __pci_restore_msix_state(struct pci_dev *dev){ int pos; struct msi_desc *entry; u16 control; if (!dev->msix_enabled) return; /* route the table */ pci_intx_for_msi(dev, 0); msix_set_enable(dev, 0); list_for_each_entry(entry, &dev->msi_list, list) { write_msi_msg(entry->irq, &entry->msg); msi_set_mask_bit(entry->irq, entry->msi_attrib.masked); } BUG_ON(list_empty(&dev->msi_list)); entry = list_entry(dev->msi_list.next, struct msi_desc, list); pos = entry->msi_attrib.pos; pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); control &= ~PCI_MSIX_FLAGS_MASKALL; control |= PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);}void pci_restore_msi_state(struct pci_dev *dev){ __pci_restore_msi_state(dev); __pci_restore_msix_state(dev);}#endif /* CONFIG_PM *//** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with a single * MSI irq, regardless of device function is capable of handling * multiple messages. A return of zero indicates the successful setup * of an entry zero with the new MSI irq or non-zero for otherwise. **/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); } 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;}/** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of struct msix_entry entries * @nvec: number of @entries * * Setup the MSI-X capability structure of device function with a
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?