msi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,148 行 · 第 1/3 页
C
1,148 行
/* * 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/mm.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/config.h>#include <linux/ioport.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <asm/errno.h>#include <asm/io.h>#include <asm/smp.h>#include "msi.h"static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED;static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };static kmem_cache_t* msi_cachep;static int pci_msi_enable = 1;static int last_alloc_vector = 0;static int nr_released_vectors = 0;static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS;static int nr_msix_devices = 0;#ifndef CONFIG_X86_IO_APICint vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };#endifstatic void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags){ memset(p, 0, NR_IRQS * sizeof(struct msi_desc));}static int msi_cache_init(void){ msi_cachep = kmem_cache_create("msi_cache", NR_IRQS * sizeof(struct msi_desc), 0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL); if (!msi_cachep) return -ENOMEM; return 0;}static void msi_set_mask_bit(unsigned int vector, int flag){ struct msi_desc *entry; entry = (struct msi_desc *)msi_desc[vector]; if (!entry || !entry->dev || !entry->mask_base) return; switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { int pos; u32 mask_bits; pos = 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); 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); break; } default: break; }}#ifdef CONFIG_SMPstatic void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask){ struct msi_desc *entry; struct msg_address address; entry = (struct msi_desc *)msi_desc[vector]; if (!entry || !entry->dev) return; switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { int pos; if (!(pos = pci_find_capability(entry->dev, PCI_CAP_ID_MSI))) return; pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), &address.lo_address.value); address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) << MSI_TARGET_CPU_SHIFT); entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask); pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), address.lo_address.value); break; } case PCI_CAP_ID_MSIX: { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; address.lo_address.value = readl(entry->mask_base + offset); address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) << MSI_TARGET_CPU_SHIFT); entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask); writel(address.lo_address.value, entry->mask_base + offset); break; } default: break; }}#ifdef CONFIG_IRQBALANCEstatic inline void move_msi(int vector){ if (!cpus_empty(pending_irq_balance_cpumask[vector])) { set_msi_affinity(vector, pending_irq_balance_cpumask[vector]); cpus_clear(pending_irq_balance_cpumask[vector]); }}#endif /* CONFIG_IRQBALANCE */#endif /* CONFIG_SMP */static void mask_MSI_irq(unsigned int vector){ msi_set_mask_bit(vector, 1);}static void unmask_MSI_irq(unsigned int vector){ msi_set_mask_bit(vector, 0);}static unsigned int startup_msi_irq_wo_maskbit(unsigned int vector){ struct msi_desc *entry; unsigned long flags; spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[vector]; if (!entry || !entry->dev) { spin_unlock_irqrestore(&msi_lock, flags); return 0; } entry->msi_attrib.state = 1; /* Mark it active */ spin_unlock_irqrestore(&msi_lock, flags); return 0; /* never anything pending */}static void release_msi(unsigned int vector);static void shutdown_msi_irq(unsigned int vector){ release_msi(vector);}#define shutdown_msi_irq_wo_maskbit shutdown_msi_irqstatic void enable_msi_irq_wo_maskbit(unsigned int vector) {}static void disable_msi_irq_wo_maskbit(unsigned int vector) {}static void ack_msi_irq_wo_maskbit(unsigned int vector) {}static void end_msi_irq_wo_maskbit(unsigned int vector){ move_msi(vector); ack_APIC_irq();}static unsigned int startup_msi_irq_w_maskbit(unsigned int vector){ struct msi_desc *entry; unsigned long flags; spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[vector]; if (!entry || !entry->dev) { spin_unlock_irqrestore(&msi_lock, flags); return 0; } entry->msi_attrib.state = 1; /* Mark it active */ spin_unlock_irqrestore(&msi_lock, flags); unmask_MSI_irq(vector); return 0; /* never anything pending */}#define shutdown_msi_irq_w_maskbit shutdown_msi_irq#define enable_msi_irq_w_maskbit unmask_MSI_irq#define disable_msi_irq_w_maskbit mask_MSI_irq#define ack_msi_irq_w_maskbit mask_MSI_irqstatic void end_msi_irq_w_maskbit(unsigned int vector){ move_msi(vector); unmask_MSI_irq(vector); ack_APIC_irq();}/* * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices, * which implement the MSI-X Capability Structure. */static struct hw_interrupt_type msix_irq_type = { .typename = "PCI-MSI-X", .startup = startup_msi_irq_w_maskbit, .shutdown = shutdown_msi_irq_w_maskbit, .enable = enable_msi_irq_w_maskbit, .disable = disable_msi_irq_w_maskbit, .ack = ack_msi_irq_w_maskbit, .end = end_msi_irq_w_maskbit, .set_affinity = set_msi_irq_affinity};/* * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, * which implement the MSI Capability Structure with * Mask-and-Pending Bits. */static struct hw_interrupt_type msi_irq_w_maskbit_type = { .typename = "PCI-MSI", .startup = startup_msi_irq_w_maskbit, .shutdown = shutdown_msi_irq_w_maskbit, .enable = enable_msi_irq_w_maskbit, .disable = disable_msi_irq_w_maskbit, .ack = ack_msi_irq_w_maskbit, .end = end_msi_irq_w_maskbit, .set_affinity = set_msi_irq_affinity};/* * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, * which implement the MSI Capability Structure without * Mask-and-Pending Bits. */static struct hw_interrupt_type msi_irq_wo_maskbit_type = { .typename = "PCI-MSI", .startup = startup_msi_irq_wo_maskbit, .shutdown = shutdown_msi_irq_wo_maskbit, .enable = enable_msi_irq_wo_maskbit, .disable = disable_msi_irq_wo_maskbit, .ack = ack_msi_irq_wo_maskbit, .end = end_msi_irq_wo_maskbit, .set_affinity = set_msi_irq_affinity};static void msi_data_init(struct msg_data *msi_data, unsigned int vector){ memset(msi_data, 0, sizeof(struct msg_data)); msi_data->vector = (u8)vector; msi_data->delivery_mode = MSI_DELIVERY_MODE; msi_data->level = MSI_LEVEL_MODE; msi_data->trigger = MSI_TRIGGER_MODE;}static void msi_address_init(struct msg_address *msi_address){ unsigned int dest_id; memset(msi_address, 0, sizeof(struct msg_address)); msi_address->hi_address = (u32)0; dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT); msi_address->lo_address.u.dest_mode = MSI_DEST_MODE; msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE; msi_address->lo_address.u.dest_id = dest_id; msi_address->lo_address.value |= (MSI_TARGET_CPU << MSI_TARGET_CPU_SHIFT);}static int msi_free_vector(struct pci_dev* dev, int vector, int reassign);static int assign_msi_vector(void){ static int new_vector_avail = 1; int vector; unsigned long flags; /* * msi_lock is provided to ensure that successful allocation of MSI * vector is assigned unique among drivers. */ spin_lock_irqsave(&msi_lock, flags); if (!new_vector_avail) { int free_vector = 0; /* * vector_irq[] = -1 indicates that this specific vector is: * - assigned for MSI (since MSI have no associated IRQ) or * - assigned for legacy if less than 16, or * - having no corresponding 1:1 vector-to-IOxAPIC IRQ mapping * vector_irq[] = 0 indicates that this vector, previously * assigned for MSI, is freed by hotplug removed operations. * This vector will be reused for any subsequent hotplug added * operations. * vector_irq[] > 0 indicates that this vector is assigned for * IOxAPIC IRQs. This vector and its value provides a 1-to-1 * vector-to-IOxAPIC IRQ mapping. */ for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) { if (vector_irq[vector] != 0) continue; free_vector = vector; if (!msi_desc[vector]) break; else continue; } if (!free_vector) { spin_unlock_irqrestore(&msi_lock, flags); return -EBUSY; } vector_irq[free_vector] = -1; nr_released_vectors--; spin_unlock_irqrestore(&msi_lock, flags); if (msi_desc[free_vector] != NULL) { struct pci_dev *dev; int tail; /* free all linked vectors before re-assign */ do { spin_lock_irqsave(&msi_lock, flags); dev = msi_desc[free_vector]->dev; tail = msi_desc[free_vector]->link.tail; spin_unlock_irqrestore(&msi_lock, flags); msi_free_vector(dev, tail, 1); } while (free_vector != tail); } return free_vector; } vector = assign_irq_vector(AUTO_ASSIGN); last_alloc_vector = vector; if (vector == LAST_DEVICE_VECTOR) new_vector_avail = 0; spin_unlock_irqrestore(&msi_lock, flags); return vector;}static int get_new_vector(void){ int vector; if ((vector = assign_msi_vector()) > 0) set_intr_gate(vector, interrupt[vector]); return vector;}static int msi_init(void){ static int status = -ENOMEM; if (!status) return status; if ((status = msi_cache_init()) < 0) { pci_msi_enable = 0; printk(KERN_INFO "WARNING: MSI INIT FAILURE\n"); return status; } last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); if (last_alloc_vector < 0) { pci_msi_enable = 0; printk(KERN_INFO "WARNING: ALL VECTORS ARE BUSY\n");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?